1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "zip_archive.h"
18
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <vector>
25
26 #include "android-base/stringprintf.h"
27 #include "ziparchive/zip_archive.h"
28
29 #include "base/mman.h"
30 #include "bit_utils.h"
31 #include "unix_file/fd_file.h"
32
33 namespace art {
34
35 // Log file contents and mmap info when mapping entries directly.
36 static constexpr const bool kDebugZipMapDirectly = false;
37
38 using android::base::StringPrintf;
39
GetUncompressedLength() const40 uint32_t ZipEntry::GetUncompressedLength() const { return zip_entry_->uncompressed_length; }
41
GetCrc32() const42 uint32_t ZipEntry::GetCrc32() const { return zip_entry_->crc32; }
43
IsUncompressed() const44 bool ZipEntry::IsUncompressed() const { return zip_entry_->method == kCompressStored; }
45
IsAlignedTo(size_t alignment) const46 bool ZipEntry::IsAlignedTo(size_t alignment) const {
47 DCHECK(IsPowerOfTwo(alignment)) << alignment;
48 return IsAlignedParam(zip_entry_->offset, static_cast<int>(alignment));
49 }
50
GetOffset() const51 off_t ZipEntry::GetOffset() const { return zip_entry_->offset; }
52
~ZipEntry()53 ZipEntry::~ZipEntry() {
54 delete zip_entry_;
55 }
56
ExtractToFile(File & file,std::string * error_msg)57 bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) {
58 const int32_t error = ExtractEntryToFile(handle_, zip_entry_, file.Fd());
59 if (error != 0) {
60 *error_msg = "Failed to extract '" + entry_name_ + "': " + ErrorCodeString(error);
61 return false;
62 }
63
64 return true;
65 }
66
ExtractToMemMap(const char * zip_filename,const char * entry_filename,std::string * error_msg)67 MemMap ZipEntry::ExtractToMemMap(const char* zip_filename,
68 const char* entry_filename,
69 std::string* error_msg) {
70 std::string name(entry_filename);
71 name += " extracted in memory from ";
72 name += zip_filename;
73 MemMap map = MemMap::MapAnonymous(name.c_str(),
74 GetUncompressedLength(),
75 PROT_READ | PROT_WRITE,
76 /*low_4gb=*/ false,
77 error_msg);
78 if (!map.IsValid()) {
79 DCHECK(!error_msg->empty());
80 return MemMap::Invalid();
81 }
82
83 DCHECK_EQ(map.Size(), GetUncompressedLength());
84 if (!ExtractToMemory(map.Begin(), error_msg)) {
85 return MemMap::Invalid();
86 }
87
88 return map;
89 }
90
ExtractToMemory(uint8_t * buffer,std::string * error_msg)91 bool ZipEntry::ExtractToMemory(/*out*/uint8_t* buffer, /*out*/std::string* error_msg) {
92 const int32_t error = ::ExtractToMemory(handle_, zip_entry_, buffer, GetUncompressedLength());
93 if (error != 0) {
94 *error_msg = "Failed to extract '" + entry_name_ + "': " + ErrorCodeString(error);
95 return false;
96 }
97 return true;
98 }
99
MapDirectlyFromFile(const char * zip_filename,std::string * error_msg)100 MemMap ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* error_msg) {
101 const int zip_fd = GetFileDescriptor(handle_);
102 const char* entry_filename = entry_name_.c_str();
103
104 // Should not happen since we don't have a memory ZipArchive constructor.
105 // However the underlying ZipArchive isn't required to have an FD,
106 // so check to be sure.
107 CHECK_GE(zip_fd, 0) <<
108 StringPrintf("Cannot map '%s' (in zip '%s') directly because the zip archive "
109 "is not file backed.",
110 entry_filename,
111 zip_filename);
112
113 if (!IsUncompressed()) {
114 *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because it is compressed.",
115 entry_filename,
116 zip_filename);
117 return MemMap::Invalid();
118 } else if (zip_entry_->uncompressed_length != zip_entry_->compressed_length) {
119 *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because "
120 "entry has bad size (%u != %u).",
121 entry_filename,
122 zip_filename,
123 zip_entry_->uncompressed_length,
124 zip_entry_->compressed_length);
125 return MemMap::Invalid();
126 }
127
128 std::string name(entry_filename);
129 name += " mapped directly in memory from ";
130 name += zip_filename;
131
132 const off_t offset = zip_entry_->offset;
133
134 if (kDebugZipMapDirectly) {
135 LOG(INFO) << "zip_archive: " << "make mmap of " << name << " @ offset = " << offset;
136 }
137
138 MemMap map =
139 MemMap::MapFile(GetUncompressedLength(), // Byte count
140 PROT_READ | PROT_WRITE,
141 MAP_PRIVATE,
142 zip_fd,
143 offset,
144 /*low_4gb=*/ false,
145 name.c_str(),
146 error_msg);
147
148 if (!map.IsValid()) {
149 DCHECK(!error_msg->empty());
150 }
151
152 if (kDebugZipMapDirectly) {
153 // Dump contents of file, same format as using this shell command:
154 // $> od -j <offset> -t x1 <zip_filename>
155 static constexpr const int kMaxDumpChars = 15;
156 lseek(zip_fd, 0, SEEK_SET);
157
158 int count = offset + kMaxDumpChars;
159
160 std::string tmp;
161 char buf;
162
163 // Dump file contents.
164 int i = 0;
165 while (read(zip_fd, &buf, 1) > 0 && i < count) {
166 tmp += StringPrintf("%3d ", (unsigned int)buf);
167 ++i;
168 }
169
170 LOG(INFO) << "map_fd raw bytes starting at 0";
171 LOG(INFO) << "" << tmp;
172 LOG(INFO) << "---------------------------";
173
174 // Dump map contents.
175 if (map.IsValid()) {
176 tmp = "";
177
178 count = kMaxDumpChars;
179
180 uint8_t* begin = map.Begin();
181 for (i = 0; i < count; ++i) {
182 tmp += StringPrintf("%3d ", (unsigned int)begin[i]);
183 }
184
185 LOG(INFO) << "map address " << StringPrintf("%p", begin);
186 LOG(INFO) << "map first " << kMaxDumpChars << " chars:";
187 LOG(INFO) << tmp;
188 }
189 }
190
191 return map;
192 }
193
MapDirectlyOrExtract(const char * zip_filename,const char * entry_filename,std::string * error_msg,size_t alignment)194 MemMap ZipEntry::MapDirectlyOrExtract(const char* zip_filename,
195 const char* entry_filename,
196 std::string* error_msg,
197 size_t alignment) {
198 if (IsUncompressed() && IsAlignedTo(alignment) && GetFileDescriptor(handle_) >= 0) {
199 std::string local_error_msg;
200 MemMap ret = MapDirectlyFromFile(zip_filename, &local_error_msg);
201 if (ret.IsValid()) {
202 return ret;
203 }
204 // Fall back to extraction for the failure case.
205 }
206 return ExtractToMemMap(zip_filename, entry_filename, error_msg);
207 }
208
SetCloseOnExec(int fd)209 static void SetCloseOnExec(int fd) {
210 #ifdef _WIN32
211 // Exec is not supported on Windows.
212 UNUSED(fd);
213 PLOG(ERROR) << "SetCloseOnExec is not supported on Windows.";
214 #else
215 // This dance is more portable than Linux's O_CLOEXEC open(2) flag.
216 int flags = fcntl(fd, F_GETFD);
217 if (flags == -1) {
218 PLOG(WARNING) << "fcntl(" << fd << ", F_GETFD) failed";
219 return;
220 }
221 int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
222 if (rc == -1) {
223 PLOG(WARNING) << "fcntl(" << fd << ", F_SETFD, " << flags << ") failed";
224 return;
225 }
226 #endif
227 }
228
Open(const char * filename,std::string * error_msg)229 ZipArchive* ZipArchive::Open(const char* filename, std::string* error_msg) {
230 DCHECK(filename != nullptr);
231
232 // Don't call into `OpenArchive` on file absence. `OpenArchive` prints a warning even if the file
233 // absence is expected.
234 if (!OS::FileExists(filename)) {
235 *error_msg = StringPrintf("Failed to open '%s': File not found", filename);
236 return nullptr;
237 }
238
239 ZipArchiveHandle handle;
240 const int32_t error = OpenArchive(filename, &handle);
241 if (error != 0) {
242 *error_msg = StringPrintf("Failed to open '%s': %s", filename, ErrorCodeString(error));
243 CloseArchive(handle);
244 return nullptr;
245 }
246
247 SetCloseOnExec(GetFileDescriptor(handle));
248 return new ZipArchive(handle);
249 }
250
OpenFromFd(int fd,const char * filename,std::string * error_msg)251 ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) {
252 return OpenFromFdInternal(fd, /*assume_ownership=*/true, filename, error_msg);
253 }
254
OpenFromOwnedFd(int fd,const char * filename,std::string * error_msg)255 ZipArchive* ZipArchive::OpenFromOwnedFd(int fd, const char* filename, std::string* error_msg) {
256 return OpenFromFdInternal(fd, /*assume_ownership=*/false, filename, error_msg);
257 }
258
OpenFromMemory(const uint8_t * data,size_t size,const char * filename,std::string * error_msg)259 ZipArchive* ZipArchive::OpenFromMemory(const uint8_t* data,
260 size_t size,
261 const char* filename,
262 std::string* error_msg) {
263 DCHECK(filename != nullptr);
264 DCHECK(data != nullptr);
265
266 ZipArchiveHandle handle;
267 const int32_t error = OpenArchiveFromMemory(data, size, filename, &handle);
268 if (error != 0) {
269 *error_msg =
270 StringPrintf("Failed to open '%s' from memory: %s", filename, ErrorCodeString(error));
271 CloseArchive(handle);
272 return nullptr;
273 }
274
275 return new ZipArchive(handle);
276 }
277
OpenFromFdInternal(int fd,bool assume_ownership,const char * filename,std::string * error_msg)278 ZipArchive* ZipArchive::OpenFromFdInternal(int fd,
279 bool assume_ownership,
280 const char* filename,
281 std::string* error_msg) {
282 DCHECK(filename != nullptr);
283 DCHECK_GT(fd, 0);
284
285 ZipArchiveHandle handle;
286 const int32_t error = OpenArchiveFd(fd, filename, &handle, assume_ownership);
287 if (error != 0) {
288 *error_msg = StringPrintf("Failed to open '%s' from fd: %s", filename, ErrorCodeString(error));
289 CloseArchive(handle);
290 return nullptr;
291 }
292
293 SetCloseOnExec(GetFileDescriptor(handle));
294 return new ZipArchive(handle);
295 }
296
Find(const char * name,std::string * error_msg) const297 ZipEntry* ZipArchive::Find(const char* name, std::string* error_msg) const {
298 return FindImpl(name, /*allow_entry_not_found=*/false, error_msg);
299 }
300
FindOrNull(const char * name,std::string * error_msg) const301 ZipEntry* ZipArchive::FindOrNull(const char* name, std::string* error_msg) const {
302 return FindImpl(name, /*allow_entry_not_found=*/true, error_msg);
303 }
304
FindImpl(const char * name,bool allow_entry_not_found,std::string * error_msg) const305 ZipEntry* ZipArchive::FindImpl(const char* name,
306 bool allow_entry_not_found,
307 std::string* error_msg) const {
308 DCHECK(name != nullptr);
309
310 // Resist the urge to delete the space. <: is a bigraph sequence.
311 std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
312 const int32_t error = FindEntry(handle_, name, zip_entry.get());
313 if (error != 0) {
314 // From system/libziparchive/zip_error.cpp.
315 constexpr std::string_view kEntryNotFound = "Entry not found";
316 if (!allow_entry_not_found || ErrorCodeString(error) != kEntryNotFound) {
317 *error_msg = StringPrintf("Failed to find entry '%s': %s", name, ErrorCodeString(error));
318 }
319 return nullptr;
320 }
321
322 return new ZipEntry(handle_, zip_entry.release(), name);
323 }
324
~ZipArchive()325 ZipArchive::~ZipArchive() {
326 CloseArchive(handle_);
327 }
328
329 } // namespace art
330