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