Bug 842681 - Refactor the linker Zip code and allow to use an existing memory buffer as a Zip file. r=mwu
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 08 Mar 2013 09:24:46 +0100
changeset 124215 f17a3abbce2400ddf7e670738c294ce9738460a0
parent 124214 b20599257ae765088e8e0e3a271e4332bec718c7
child 124216 eec1db8d628402dab17f675342ec71cbabd10995
push id1412
push userttaubert@mozilla.com
push dateMon, 11 Mar 2013 12:01:17 +0000
treeherderfx-team@1176278e959b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwu
bugs842681
milestone22.0a1
Bug 842681 - Refactor the linker Zip code and allow to use an existing memory buffer as a Zip file. r=mwu
mozglue/android/APKOpen.cpp
mozglue/linker/ElfLoader.cpp
mozglue/linker/ElfLoader.h
mozglue/linker/Zip.cpp
mozglue/linker/Zip.h
mozglue/tests/TestZip.cpp
--- 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;
 }