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()40 uint32_t ZipEntry::GetUncompressedLength() {
41   return zip_entry_->uncompressed_length;
42 }
43 
GetCrc32()44 uint32_t ZipEntry::GetCrc32() {
45   return zip_entry_->crc32;
46 }
47 
IsUncompressed()48 bool ZipEntry::IsUncompressed() {
49   return zip_entry_->method == kCompressStored;
50 }
51 
IsAlignedTo(size_t alignment) const52 bool ZipEntry::IsAlignedTo(size_t alignment) const {
53   DCHECK(IsPowerOfTwo(alignment)) << alignment;
54   return IsAlignedParam(zip_entry_->offset, static_cast<int>(alignment));
55 }
56 
~ZipEntry()57 ZipEntry::~ZipEntry() {
58   delete zip_entry_;
59 }
60 
ExtractToFile(File & file,std::string * error_msg)61 bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) {
62   const int32_t error = ExtractEntryToFile(handle_, zip_entry_, file.Fd());
63   if (error) {
64     *error_msg = std::string(ErrorCodeString(error));
65     return false;
66   }
67 
68   return true;
69 }
70 
ExtractToMemMap(const char * zip_filename,const char * entry_filename,std::string * error_msg)71 MemMap ZipEntry::ExtractToMemMap(const char* zip_filename,
72                                  const char* entry_filename,
73                                  std::string* error_msg) {
74   std::string name(entry_filename);
75   name += " extracted in memory from ";
76   name += zip_filename;
77   MemMap map = MemMap::MapAnonymous(name.c_str(),
78                                     GetUncompressedLength(),
79                                     PROT_READ | PROT_WRITE,
80                                     /*low_4gb=*/ false,
81                                     error_msg);
82   if (!map.IsValid()) {
83     DCHECK(!error_msg->empty());
84     return MemMap::Invalid();
85   }
86 
87   const int32_t error = ExtractToMemory(handle_, zip_entry_, map.Begin(), map.Size());
88   if (error) {
89     *error_msg = std::string(ErrorCodeString(error));
90     return MemMap::Invalid();
91   }
92 
93   return map;
94 }
95 
MapDirectlyFromFile(const char * zip_filename,std::string * error_msg)96 MemMap ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* error_msg) {
97   const int zip_fd = GetFileDescriptor(handle_);
98   const char* entry_filename = entry_name_.c_str();
99 
100   // Should not happen since we don't have a memory ZipArchive constructor.
101   // However the underlying ZipArchive isn't required to have an FD,
102   // so check to be sure.
103   CHECK_GE(zip_fd, 0) <<
104       StringPrintf("Cannot map '%s' (in zip '%s') directly because the zip archive "
105                    "is not file backed.",
106                    entry_filename,
107                    zip_filename);
108 
109   if (!IsUncompressed()) {
110     *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because it is compressed.",
111                               entry_filename,
112                               zip_filename);
113     return MemMap::Invalid();
114   } else if (zip_entry_->uncompressed_length != zip_entry_->compressed_length) {
115     *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because "
116                               "entry has bad size (%u != %u).",
117                               entry_filename,
118                               zip_filename,
119                               zip_entry_->uncompressed_length,
120                               zip_entry_->compressed_length);
121     return MemMap::Invalid();
122   }
123 
124   std::string name(entry_filename);
125   name += " mapped directly in memory from ";
126   name += zip_filename;
127 
128   const off_t offset = zip_entry_->offset;
129 
130   if (kDebugZipMapDirectly) {
131     LOG(INFO) << "zip_archive: " << "make mmap of " << name << " @ offset = " << offset;
132   }
133 
134   MemMap map =
135       MemMap::MapFile(GetUncompressedLength(),  // Byte count
136                       PROT_READ | PROT_WRITE,
137                       MAP_PRIVATE,
138                       zip_fd,
139                       offset,
140                       /*low_4gb=*/ false,
141                       name.c_str(),
142                       error_msg);
143 
144   if (!map.IsValid()) {
145     DCHECK(!error_msg->empty());
146   }
147 
148   if (kDebugZipMapDirectly) {
149     // Dump contents of file, same format as using this shell command:
150     // $> od -j <offset> -t x1 <zip_filename>
151     static constexpr const int kMaxDumpChars = 15;
152     lseek(zip_fd, 0, SEEK_SET);
153 
154     int count = offset + kMaxDumpChars;
155 
156     std::string tmp;
157     char buf;
158 
159     // Dump file contents.
160     int i = 0;
161     while (read(zip_fd, &buf, 1) > 0 && i < count) {
162       tmp += StringPrintf("%3d ", (unsigned int)buf);
163       ++i;
164     }
165 
166     LOG(INFO) << "map_fd raw bytes starting at 0";
167     LOG(INFO) << "" << tmp;
168     LOG(INFO) << "---------------------------";
169 
170     // Dump map contents.
171     if (map.IsValid()) {
172       tmp = "";
173 
174       count = kMaxDumpChars;
175 
176       uint8_t* begin = map.Begin();
177       for (i = 0; i < count; ++i) {
178         tmp += StringPrintf("%3d ", (unsigned int)begin[i]);
179       }
180 
181       LOG(INFO) << "map address " << StringPrintf("%p", begin);
182       LOG(INFO) << "map first " << kMaxDumpChars << " chars:";
183       LOG(INFO) << tmp;
184     }
185   }
186 
187   return map;
188 }
189 
MapDirectlyOrExtract(const char * zip_filename,const char * entry_filename,std::string * error_msg,size_t alignment)190 MemMap ZipEntry::MapDirectlyOrExtract(const char* zip_filename,
191                                       const char* entry_filename,
192                                       std::string* error_msg,
193                                       size_t alignment) {
194   if (IsUncompressed() && IsAlignedTo(alignment) && GetFileDescriptor(handle_) >= 0) {
195     std::string local_error_msg;
196     MemMap ret = MapDirectlyFromFile(zip_filename, &local_error_msg);
197     if (ret.IsValid()) {
198       return ret;
199     }
200     // Fall back to extraction for the failure case.
201   }
202   return ExtractToMemMap(zip_filename, entry_filename, error_msg);
203 }
204 
SetCloseOnExec(int fd)205 static void SetCloseOnExec(int fd) {
206 #ifdef _WIN32
207   // Exec is not supported on Windows.
208   UNUSED(fd);
209   PLOG(ERROR) << "SetCloseOnExec is not supported on Windows.";
210 #else
211   // This dance is more portable than Linux's O_CLOEXEC open(2) flag.
212   int flags = fcntl(fd, F_GETFD);
213   if (flags == -1) {
214     PLOG(WARNING) << "fcntl(" << fd << ", F_GETFD) failed";
215     return;
216   }
217   int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
218   if (rc == -1) {
219     PLOG(WARNING) << "fcntl(" << fd << ", F_SETFD, " << flags << ") failed";
220     return;
221   }
222 #endif
223 }
224 
Open(const char * filename,std::string * error_msg)225 ZipArchive* ZipArchive::Open(const char* filename, std::string* error_msg) {
226   DCHECK(filename != nullptr);
227 
228   ZipArchiveHandle handle;
229   const int32_t error = OpenArchive(filename, &handle);
230   if (error) {
231     *error_msg = std::string(ErrorCodeString(error));
232     CloseArchive(handle);
233     return nullptr;
234   }
235 
236   SetCloseOnExec(GetFileDescriptor(handle));
237   return new ZipArchive(handle);
238 }
239 
OpenFromFd(int fd,const char * filename,std::string * error_msg)240 ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) {
241   DCHECK(filename != nullptr);
242   DCHECK_GT(fd, 0);
243 
244   ZipArchiveHandle handle;
245   const int32_t error = OpenArchiveFd(fd, filename, &handle);
246   if (error) {
247     *error_msg = std::string(ErrorCodeString(error));
248     CloseArchive(handle);
249     return nullptr;
250   }
251 
252   SetCloseOnExec(GetFileDescriptor(handle));
253   return new ZipArchive(handle);
254 }
255 
Find(const char * name,std::string * error_msg) const256 ZipEntry* ZipArchive::Find(const char* name, std::string* error_msg) const {
257   DCHECK(name != nullptr);
258 
259   // Resist the urge to delete the space. <: is a bigraph sequence.
260   std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
261   const int32_t error = FindEntry(handle_, name, zip_entry.get());
262   if (error) {
263     *error_msg = std::string(ErrorCodeString(error));
264     return nullptr;
265   }
266 
267   return new ZipEntry(handle_, zip_entry.release(), name);
268 }
269 
~ZipArchive()270 ZipArchive::~ZipArchive() {
271   CloseArchive(handle_);
272 }
273 
274 }  // namespace art
275