• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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