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