mozglue/linker/Zip.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include <sys/mman.h>
michael@0 6 #include <sys/stat.h>
michael@0 7 #include <fcntl.h>
michael@0 8 #include <errno.h>
michael@0 9 #include <unistd.h>
michael@0 10 #include <cstdlib>
michael@0 11 #include <algorithm>
michael@0 12 #include "Logging.h"
michael@0 13 #include "Zip.h"
michael@0 14
michael@0 15 mozilla::TemporaryRef<Zip>
michael@0 16 Zip::Create(const char *filename)
michael@0 17 {
michael@0 18 /* Open and map the file in memory */
michael@0 19 AutoCloseFD fd(open(filename, O_RDONLY));
michael@0 20 if (fd == -1) {
michael@0 21 LOG("Error opening %s: %s", filename, strerror(errno));
michael@0 22 return nullptr;
michael@0 23 }
michael@0 24 struct stat st;
michael@0 25 if (fstat(fd, &st) == -1) {
michael@0 26 LOG("Error stating %s: %s", filename, strerror(errno));
michael@0 27 return nullptr;
michael@0 28 }
michael@0 29 size_t size = st.st_size;
michael@0 30 if (size <= sizeof(CentralDirectoryEnd)) {
michael@0 31 LOG("Error reading %s: too short", filename);
michael@0 32 return nullptr;
michael@0 33 }
michael@0 34 void *mapped = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
michael@0 35 if (mapped == MAP_FAILED) {
michael@0 36 LOG("Error mmapping %s: %s", filename, strerror(errno));
michael@0 37 return nullptr;
michael@0 38 }
michael@0 39 DEBUG_LOG("Mapped %s @%p", filename, mapped);
michael@0 40
michael@0 41 return Create(filename, mapped, size);
michael@0 42 }
michael@0 43
michael@0 44 mozilla::TemporaryRef<Zip>
michael@0 45 Zip::Create(const char *filename, void *mapped, size_t size)
michael@0 46 {
michael@0 47 mozilla::RefPtr<Zip> zip = new Zip(filename, mapped, size);
michael@0 48
michael@0 49 // If neither the first Local File entry nor central directory entries
michael@0 50 // have been found, the zip was invalid.
michael@0 51 if (!zip->nextFile && !zip->entries) {
michael@0 52 LOG("%s - Invalid zip", filename);
michael@0 53 return nullptr;
michael@0 54 }
michael@0 55
michael@0 56 ZipCollection::Singleton.Register(zip);
michael@0 57 return zip;
michael@0 58 }
michael@0 59
michael@0 60 Zip::Zip(const char *filename, void *mapped, size_t size)
michael@0 61 : name(filename ? strdup(filename) : nullptr)
michael@0 62 , mapped(mapped)
michael@0 63 , size(size)
michael@0 64 , nextFile(LocalFile::validate(mapped)) // first Local File entry
michael@0 65 , nextDir(nullptr)
michael@0 66 , entries(nullptr)
michael@0 67 {
michael@0 68 // If the first local file entry couldn't be found (which can happen
michael@0 69 // with optimized jars), check the first central directory entry.
michael@0 70 if (!nextFile)
michael@0 71 GetFirstEntry();
michael@0 72 }
michael@0 73
michael@0 74 Zip::~Zip()
michael@0 75 {
michael@0 76 ZipCollection::Forget(this);
michael@0 77 if (name) {
michael@0 78 munmap(mapped, size);
michael@0 79 DEBUG_LOG("Unmapped %s @%p", name, mapped);
michael@0 80 free(name);
michael@0 81 }
michael@0 82 }
michael@0 83
michael@0 84 bool
michael@0 85 Zip::GetStream(const char *path, Zip::Stream *out) const
michael@0 86 {
michael@0 87 DEBUG_LOG("%s - GetFile %s", name, path);
michael@0 88 /* Fast path: if the Local File header on store matches, we can return the
michael@0 89 * corresponding stream right away.
michael@0 90 * However, the Local File header may not contain enough information, in
michael@0 91 * which case the 3rd bit on the generalFlag is set. Unfortunately, this
michael@0 92 * bit is also set in some archives even when we do have the data (most
michael@0 93 * notably the android packages as built by the Mozilla build system).
michael@0 94 * So instead of testing the generalFlag bit, only use the fast path when
michael@0 95 * we haven't read the central directory entries yet, and when the
michael@0 96 * compressed size as defined in the header is not filled (which is a
michael@0 97 * normal condition for the bit to be set). */
michael@0 98 if (nextFile && nextFile->GetName().Equals(path) &&
michael@0 99 !entries && (nextFile->compressedSize != 0)) {
michael@0 100 DEBUG_LOG("%s - %s was next file: fast path", name, path);
michael@0 101 /* Fill Stream info from Local File header content */
michael@0 102 const char *data = reinterpret_cast<const char *>(nextFile->GetData());
michael@0 103 out->compressedBuf = data;
michael@0 104 out->compressedSize = nextFile->compressedSize;
michael@0 105 out->uncompressedSize = nextFile->uncompressedSize;
michael@0 106 out->type = static_cast<Stream::Type>(uint16_t(nextFile->compression));
michael@0 107
michael@0 108 /* Find the next Local File header. It is usually simply following the
michael@0 109 * compressed stream, but in cases where the 3rd bit of the generalFlag
michael@0 110 * is set, there is a Data Descriptor header before. */
michael@0 111 data += nextFile->compressedSize;
michael@0 112 if ((nextFile->generalFlag & 0x8) && DataDescriptor::validate(data)) {
michael@0 113 data += sizeof(DataDescriptor);
michael@0 114 }
michael@0 115 nextFile = LocalFile::validate(data);
michael@0 116 return true;
michael@0 117 }
michael@0 118
michael@0 119 /* If the directory entry we have in store doesn't match, scan the Central
michael@0 120 * Directory for the entry corresponding to the given path */
michael@0 121 if (!nextDir || !nextDir->GetName().Equals(path)) {
michael@0 122 const DirectoryEntry *entry = GetFirstEntry();
michael@0 123 DEBUG_LOG("%s - Scan directory entries in search for %s", name, path);
michael@0 124 while (entry && !entry->GetName().Equals(path)) {
michael@0 125 entry = entry->GetNext();
michael@0 126 }
michael@0 127 nextDir = entry;
michael@0 128 }
michael@0 129 if (!nextDir) {
michael@0 130 DEBUG_LOG("%s - Couldn't find %s", name, path);
michael@0 131 return false;
michael@0 132 }
michael@0 133
michael@0 134 /* Find the Local File header corresponding to the Directory entry that
michael@0 135 * was found. */
michael@0 136 nextFile = LocalFile::validate(static_cast<const char *>(mapped)
michael@0 137 + nextDir->offset);
michael@0 138 if (!nextFile) {
michael@0 139 LOG("%s - Couldn't find the Local File header for %s", name, path);
michael@0 140 return false;
michael@0 141 }
michael@0 142
michael@0 143 /* Fill Stream info from Directory entry content */
michael@0 144 const char *data = reinterpret_cast<const char *>(nextFile->GetData());
michael@0 145 out->compressedBuf = data;
michael@0 146 out->compressedSize = nextDir->compressedSize;
michael@0 147 out->uncompressedSize = nextDir->uncompressedSize;
michael@0 148 out->type = static_cast<Stream::Type>(uint16_t(nextDir->compression));
michael@0 149
michael@0 150 /* Store the next directory entry */
michael@0 151 nextDir = nextDir->GetNext();
michael@0 152 nextFile = nullptr;
michael@0 153 return true;
michael@0 154 }
michael@0 155
michael@0 156 const Zip::DirectoryEntry *
michael@0 157 Zip::GetFirstEntry() const
michael@0 158 {
michael@0 159 if (entries)
michael@0 160 return entries;
michael@0 161
michael@0 162 const CentralDirectoryEnd *end = nullptr;
michael@0 163 const char *_end = static_cast<const char *>(mapped) + size
michael@0 164 - sizeof(CentralDirectoryEnd);
michael@0 165
michael@0 166 /* Scan for the Central Directory End */
michael@0 167 for (; _end > mapped && !end; _end--)
michael@0 168 end = CentralDirectoryEnd::validate(_end);
michael@0 169 if (!end) {
michael@0 170 LOG("%s - Couldn't find end of central directory record", name);
michael@0 171 return nullptr;
michael@0 172 }
michael@0 173
michael@0 174 entries = DirectoryEntry::validate(static_cast<const char *>(mapped)
michael@0 175 + end->offset);
michael@0 176 if (!entries) {
michael@0 177 LOG("%s - Couldn't find central directory record", name);
michael@0 178 }
michael@0 179 return entries;
michael@0 180 }
michael@0 181
michael@0 182 ZipCollection ZipCollection::Singleton;
michael@0 183
michael@0 184 mozilla::TemporaryRef<Zip>
michael@0 185 ZipCollection::GetZip(const char *path)
michael@0 186 {
michael@0 187 /* Search the list of Zips we already have for a match */
michael@0 188 for (std::vector<Zip *>::iterator it = Singleton.zips.begin();
michael@0 189 it < Singleton.zips.end(); ++it) {
michael@0 190 if ((*it)->GetName() && (strcmp((*it)->GetName(), path) == 0))
michael@0 191 return *it;
michael@0 192 }
michael@0 193 return Zip::Create(path);
michael@0 194 }
michael@0 195
michael@0 196 void
michael@0 197 ZipCollection::Register(Zip *zip)
michael@0 198 {
michael@0 199 Singleton.zips.push_back(zip);
michael@0 200 }
michael@0 201
michael@0 202 void
michael@0 203 ZipCollection::Forget(Zip *zip)
michael@0 204 {
michael@0 205 DEBUG_LOG("ZipCollection::Forget(\"%s\")", zip->GetName());
michael@0 206 std::vector<Zip *>::iterator it = std::find(Singleton.zips.begin(),
michael@0 207 Singleton.zips.end(), zip);
michael@0 208 if (*it == zip) {
michael@0 209 Singleton.zips.erase(it);
michael@0 210 } else {
michael@0 211 DEBUG_LOG("ZipCollection::Forget: didn't find \"%s\" in bookkeeping", zip->GetName());
michael@0 212 }
michael@0 213 }

mercurial