Bug 842681 - Refactor the linker Zip code and allow to use an existing memory buffer as a Zip file. r=mwu
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -207,17 +207,17 @@ static mozglueresult
loadGeckoLibs(const char *apkName)
{
chdir(getenv("GRE_HOME"));
MOZTime t0 = MOZ_Now();
struct rusage usage1;
getrusage(RUSAGE_THREAD, &usage1);
- RefPtr<Zip> zip = new Zip(apkName);
+ RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
#ifdef MOZ_CRASHREPORTER
file_ids = (char *)extractBuf("lib.id", zip);
#endif
char *file = new char[strlen(apkName) + sizeof("!/libxpcom.so")];
sprintf(file, "%s!/libxpcom.so", apkName);
__wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
@@ -256,17 +256,17 @@ loadGeckoLibs(const char *apkName)
XRE_StartupTimelineRecord(LIBRARIES_LOADED, t1);
return SUCCESS;
}
static int loadSQLiteLibs(const char *apkName)
{
chdir(getenv("GRE_HOME"));
- RefPtr<Zip> zip = new Zip(apkName);
+ RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
if (!lib_mapping) {
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
}
#ifdef MOZ_CRASHREPORTER
file_ids = (char *)extractBuf("lib.id", zip);
#endif
@@ -289,17 +289,17 @@ static int loadSQLiteLibs(const char *ap
return SUCCESS;
}
static mozglueresult
loadNSSLibs(const char *apkName)
{
chdir(getenv("GRE_HOME"));
- RefPtr<Zip> zip = new Zip(apkName);
+ RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
if (!lib_mapping) {
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
}
#ifdef MOZ_CRASHREPORTER
file_ids = (char *)extractBuf("lib.id", zip);
#endif
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -255,17 +255,17 @@ ElfLoader::Load(const char *path, int fl
* try to load the directory/lib.so in /foo/bar/baz/archive, provided
* that file is a Zip archive. */
Mappable *mappable = NULL;
RefPtr<Zip> zip;
const char *subpath;
if ((subpath = strchr(path, '!'))) {
char *zip_path = strndup(path, subpath - path);
while (*(++subpath) == '/') { }
- zip = zips.GetZip(zip_path);
+ zip = ZipCollection::GetZip(zip_path);
Zip::Stream s;
if (zip && zip->GetStream(subpath, &s)) {
/* When the MOZ_LINKER_EXTRACT environment variable is set to "1",
* compressed libraries are going to be (temporarily) extracted as
* files, in the directory pointed by the MOZ_LINKER_CACHE
* environment variable. */
const char *extract = getenv("MOZ_LINKER_EXTRACT");
if (extract && !strncmp(extract, "1", 2 /* Including '\0' */))
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -407,19 +407,16 @@ protected:
void *object;
void *dso_handle;
};
private:
/* Keep track of all registered destructors */
std::vector<DestructorCaller> destructors;
- /* Keep track of Zips used for library loading */
- ZipCollection zips;
-
/* Forward declaration, see further below */
class DebuggerHelper;
public:
/* Loaded object descriptor for the debugger interface below*/
struct link_map {
/* Base address of the loaded object. */
const void *l_addr;
/* File name */
--- a/mozglue/linker/Zip.cpp
+++ b/mozglue/linker/Zip.cpp
@@ -7,59 +7,83 @@
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <cstdlib>
#include <algorithm>
#include "Logging.h"
#include "Zip.h"
-Zip::Zip(const char *filename, ZipCollection *collection)
-: name(strdup(filename))
-, mapped(MAP_FAILED)
-, nextDir(NULL)
-, entries(NULL)
-, parent(collection)
+mozilla::TemporaryRef<Zip>
+Zip::Create(const char *filename)
{
/* Open and map the file in memory */
- AutoCloseFD fd(open(name, O_RDONLY));
+ AutoCloseFD fd(open(filename, O_RDONLY));
if (fd == -1) {
log("Error opening %s: %s", filename, strerror(errno));
- return;
+ return NULL;
}
struct stat st;
if (fstat(fd, &st) == -1) {
log("Error stating %s: %s", filename, strerror(errno));
- return;
+ return NULL;
}
- size = st.st_size;
+ size_t size = st.st_size;
if (size <= sizeof(CentralDirectoryEnd)) {
log("Error reading %s: too short", filename);
- return;
+ return NULL;
}
- mapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ void *mapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
log("Error mmapping %s: %s", filename, strerror(errno));
- return;
+ return NULL;
}
debug("Mapped %s @%p", filename, mapped);
- /* Store the first Local File entry */
- nextFile = LocalFile::validate(mapped);
+ return Create(filename, mapped, size);
+}
+
+mozilla::TemporaryRef<Zip>
+Zip::Create(const char *filename, void *mapped, size_t size)
+{
+ mozilla::RefPtr<Zip> zip = new Zip(filename, mapped, size);
+
+ // If neither the first Local File entry nor central directory entries
+ // have been found, the zip was invalid.
+ if (!zip->nextFile && !zip->entries) {
+ log("%s - Invalid zip", filename);
+ return NULL;
+ }
+
+ ZipCollection::Singleton.Register(zip);
+ return zip;
+}
+
+Zip::Zip(const char *filename, void *mapped, size_t size)
+: name(filename ? strdup(filename) : NULL)
+, mapped(mapped)
+, size(size)
+, nextFile(LocalFile::validate(mapped)) // first Local File entry
+, nextDir(NULL)
+, entries(NULL)
+{
+ // If the first local file entry couldn't be found (which can happen
+ // with optimized jars), check the first central directory entry.
+ if (!nextFile)
+ GetFirstEntry();
}
Zip::~Zip()
{
- if (parent)
- parent->Forget(this);
- if (mapped != MAP_FAILED) {
+ ZipCollection::Forget(this);
+ if (name) {
munmap(mapped, size);
debug("Unmapped %s @%p", name, mapped);
+ free(name);
}
- free(name);
}
bool
Zip::GetStream(const char *path, Zip::Stream *out) const
{
debug("%s - GetFile %s", name, path);
/* Fast path: if the Local File header on store matches, we can return the
* corresponding stream right away.
@@ -127,18 +151,18 @@ Zip::GetStream(const char *path, Zip::St
nextDir = nextDir->GetNext();
nextFile = NULL;
return true;
}
const Zip::DirectoryEntry *
Zip::GetFirstEntry() const
{
- if (entries || mapped == MAP_FAILED)
- return entries; // entries is NULL in the second case above
+ if (entries)
+ return entries;
const CentralDirectoryEnd *end = NULL;
const char *_end = static_cast<const char *>(mapped) + size
- sizeof(CentralDirectoryEnd);
/* Scan for the Central Directory End */
for (; _end > mapped && !end; _end--)
end = CentralDirectoryEnd::validate(_end);
@@ -150,32 +174,40 @@ Zip::GetFirstEntry() const
entries = DirectoryEntry::validate(static_cast<const char *>(mapped)
+ end->offset);
if (!entries) {
log("%s - Couldn't find central directory record", name);
}
return entries;
}
+ZipCollection ZipCollection::Singleton;
+
mozilla::TemporaryRef<Zip>
ZipCollection::GetZip(const char *path)
{
/* Search the list of Zips we already have for a match */
- for (std::vector<Zip *>::iterator it = zips.begin(); it < zips.end(); ++it) {
- if (strcmp((*it)->GetName(), path) == 0)
+ for (std::vector<Zip *>::iterator it = Singleton.zips.begin();
+ it < Singleton.zips.end(); ++it) {
+ if ((*it)->GetName() && (strcmp((*it)->GetName(), path) == 0))
return *it;
}
- Zip *zip = new Zip(path, this);
- zips.push_back(zip);
- return zip;
+ return Zip::Create(path);
+}
+
+void
+ZipCollection::Register(Zip *zip)
+{
+ Singleton.zips.push_back(zip);
}
void
ZipCollection::Forget(Zip *zip)
{
debug("ZipCollection::Forget(\"%s\")", zip->GetName());
- std::vector<Zip *>::iterator it = std::find(zips.begin(), zips.end(), zip);
+ std::vector<Zip *>::iterator it = std::find(Singleton.zips.begin(),
+ Singleton.zips.end(), zip);
if (*it == zip) {
- zips.erase(it);
+ Singleton.zips.erase(it);
} else {
debug("ZipCollection::Forget: didn't find \"%s\" in bookkeeping", zip->GetName());
}
}
--- a/mozglue/linker/Zip.h
+++ b/mozglue/linker/Zip.h
@@ -25,21 +25,38 @@ class ZipCollection;
* code fail in bad ways. However, since the only intended use is to load
* libraries from Zip archives, there is no interest in making this code
* safe, since the libraries could contain malicious code anyways.
*/
class Zip: public mozilla::RefCounted<Zip>
{
public:
/**
- * Create a Zip instance for the given file name. In case of error, the
- * Zip instance is still created but methods will error out.
+ * Create a Zip instance for the given file name. Returns NULL in case
+ * of failure.
+ */
+ static mozilla::TemporaryRef<Zip> Create(const char *filename);
+
+ /**
+ * Create a Zip instance using the given buffer.
*/
- Zip(const char *filename, ZipCollection *collection = NULL);
+ static mozilla::TemporaryRef<Zip> Create(void *buffer, size_t size) {
+ return Create(NULL, buffer, size);
+ }
+private:
+ static mozilla::TemporaryRef<Zip> Create(const char *filename,
+ void *buffer, size_t size);
+
+ /**
+ * Private constructor
+ */
+ Zip(const char *filename, void *buffer, size_t size);
+
+public:
/**
* Destructor
*/
~Zip();
/**
* Class used to access Zip archive item streams
*/
@@ -298,39 +315,44 @@ private:
* requested entry is that one. */
mutable const LocalFile *nextFile;
/* Likewise for the next Directory entry */
mutable const DirectoryEntry *nextDir;
/* Pointer to the Directory entries */
mutable const DirectoryEntry *entries;
-
- /* ZipCollection containing this Zip */
- mutable ZipCollection *parent;
};
/**
* Class for bookkeeping Zip instances
*/
class ZipCollection
{
public:
+ static ZipCollection Singleton;
+
/**
* Get a Zip instance for the given path. If there is an existing one
* already, return that one, otherwise create a new one.
*/
- mozilla::TemporaryRef<Zip> GetZip(const char *path);
+ static mozilla::TemporaryRef<Zip> GetZip(const char *path);
protected:
+ friend class Zip;
+ /**
+ * Register the given Zip instance. This method is meant to be called
+ * by Zip::Create.
+ */
+ static void Register(Zip *zip);
+
/**
* Forget about the given Zip instance. This method is meant to be called
* by the Zip destructor.
*/
- friend Zip::~Zip();
- void Forget(Zip *zip);
+ static void Forget(Zip *zip);
private:
/* Zip instances bookkept in this collection */
std::vector<Zip *> zips;
};
#endif /* Zip_h */
--- a/mozglue/tests/TestZip.cpp
+++ b/mozglue/tests/TestZip.cpp
@@ -1,15 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <cstdio>
#include <unistd.h>
#include "Zip.h"
+#include "mozilla/RefPtr.h"
extern "C" void report_mapping() { }
/**
* test.zip is a basic test zip file with a central directory. It contains
* four entries, in the following order:
* "foo", "bar", "baz", "qux".
* The entries are going to be read out of order.
@@ -39,29 +40,29 @@ const char *no_central_dir_entries[] = {
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "TEST-FAIL | TestZip | Expecting the directory containing test Zips\n");
return 1;
}
chdir(argv[1]);
Zip::Stream s;
- Zip z("test.zip");
+ mozilla::RefPtr<Zip> z = ZipCollection::GetZip("test.zip");
for (size_t i = 0; i < sizeof(test_entries) / sizeof(*test_entries); i++) {
- if (!z.GetStream(test_entries[i], &s)) {
+ if (!z->GetStream(test_entries[i], &s)) {
fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | test.zip: Couldn't get entry \"%s\"\n", test_entries[i]);
return 1;
}
}
fprintf(stderr, "TEST-PASS | TestZip | test.zip could be accessed fully\n");
- Zip z2("no_central_dir.zip");
+ z = ZipCollection::GetZip("no_central_dir.zip");
for (size_t i = 0; i < sizeof(no_central_dir_entries)
/ sizeof(*no_central_dir_entries); i++) {
- if (!z2.GetStream(no_central_dir_entries[i], &s)) {
+ if (!z->GetStream(no_central_dir_entries[i], &s)) {
fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | no_central_dir.zip: Couldn't get entry \"%s\"\n", no_central_dir_entries[i]);
return 1;
}
}
fprintf(stderr, "TEST-PASS | TestZip | no_central_dir.zip could be accessed in order\n");
return 0;
}