Wed, 31 Dec 2014 06:09:35 +0100
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 | } |