Home | History | Annotate | Download | only in runtime
      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 "ziparchive/zip_archive.h"
     29 
     30 #include "base/bit_utils.h"
     31 #include "base/unix_file/fd_file.h"
     32 #include "dex/dex_file.h"
     33 
     34 namespace art {
     35 
     36 // Log file contents and mmap info when mapping entries directly.
     37 static constexpr const bool kDebugZipMapDirectly = false;
     38 
     39 using android::base::StringPrintf;
     40 
     41 uint32_t ZipEntry::GetUncompressedLength() {
     42   return zip_entry_->uncompressed_length;
     43 }
     44 
     45 uint32_t ZipEntry::GetCrc32() {
     46   return zip_entry_->crc32;
     47 }
     48 
     49 bool ZipEntry::IsUncompressed() {
     50   return zip_entry_->method == kCompressStored;
     51 }
     52 
     53 bool ZipEntry::IsAlignedTo(size_t alignment) const {
     54   DCHECK(IsPowerOfTwo(alignment)) << alignment;
     55   return IsAlignedParam(zip_entry_->offset, static_cast<int>(alignment));
     56 }
     57 
     58 bool ZipEntry::IsAlignedToDexHeader() const {
     59   return IsAlignedTo(alignof(DexFile::Header));
     60 }
     61 
     62 ZipEntry::~ZipEntry() {
     63   delete zip_entry_;
     64 }
     65 
     66 bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) {
     67   const int32_t error = ExtractEntryToFile(handle_, zip_entry_, file.Fd());
     68   if (error) {
     69     *error_msg = std::string(ErrorCodeString(error));
     70     return false;
     71   }
     72 
     73   return true;
     74 }
     75 
     76 MemMap* ZipEntry::ExtractToMemMap(const char* zip_filename, const char* entry_filename,
     77                                   std::string* error_msg) {
     78   std::string name(entry_filename);
     79   name += " extracted in memory from ";
     80   name += zip_filename;
     81   std::unique_ptr<MemMap> map(MemMap::MapAnonymous(name.c_str(),
     82                                                    nullptr, GetUncompressedLength(),
     83                                                    PROT_READ | PROT_WRITE, false, false,
     84                                                    error_msg));
     85   if (map.get() == nullptr) {
     86     DCHECK(!error_msg->empty());
     87     return nullptr;
     88   }
     89 
     90   const int32_t error = ExtractToMemory(handle_, zip_entry_,
     91                                         map->Begin(), map->Size());
     92   if (error) {
     93     *error_msg = std::string(ErrorCodeString(error));
     94     return nullptr;
     95   }
     96 
     97   return map.release();
     98 }
     99 
    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 nullptr;
    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 nullptr;
    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   std::unique_ptr<MemMap> map(
    139       MemMap::MapFileAtAddress(nullptr,  // Expected pointer address
    140                                GetUncompressedLength(),  // Byte count
    141                                PROT_READ | PROT_WRITE,
    142                                MAP_PRIVATE,
    143                                zip_fd,
    144                                offset,
    145                                false,  // Don't restrict allocation to lower4GB
    146                                false,  // Doesn't overlap existing map (reuse=false)
    147                                name.c_str(),
    148                                /*out*/error_msg));
    149 
    150   if (map == nullptr) {
    151     DCHECK(!error_msg->empty());
    152   }
    153 
    154   if (kDebugZipMapDirectly) {
    155     // Dump contents of file, same format as using this shell command:
    156     // $> od -j <offset> -t x1 <zip_filename>
    157     static constexpr const int kMaxDumpChars = 15;
    158     lseek(zip_fd, 0, SEEK_SET);
    159 
    160     int count = offset + kMaxDumpChars;
    161 
    162     std::string tmp;
    163     char buf;
    164 
    165     // Dump file contents.
    166     int i = 0;
    167     while (read(zip_fd, &buf, 1) > 0 && i < count) {
    168       tmp += StringPrintf("%3d ", (unsigned int)buf);
    169       ++i;
    170     }
    171 
    172     LOG(INFO) << "map_fd raw bytes starting at 0";
    173     LOG(INFO) << "" << tmp;
    174     LOG(INFO) << "---------------------------";
    175 
    176     // Dump map contents.
    177     if (map != nullptr) {
    178       tmp = "";
    179 
    180       count = kMaxDumpChars;
    181 
    182       uint8_t* begin = map->Begin();
    183       for (i = 0; i < count; ++i) {
    184         tmp += StringPrintf("%3d ", (unsigned int)begin[i]);
    185       }
    186 
    187       LOG(INFO) << "map address " << StringPrintf("%p", begin);
    188       LOG(INFO) << "map first " << kMaxDumpChars << " chars:";
    189       LOG(INFO) << tmp;
    190     }
    191   }
    192 
    193   return map.release();
    194 }
    195 
    196 MemMap* ZipEntry::MapDirectlyOrExtract(const char* zip_filename,
    197                                        const char* entry_filename,
    198                                        std::string* error_msg) {
    199   if (IsUncompressed() && GetFileDescriptor(handle_) >= 0) {
    200     MemMap* ret = MapDirectlyFromFile(zip_filename, error_msg);
    201     if (ret != nullptr) {
    202       return ret;
    203     }
    204   }
    205   // Fall back to extraction for the failure case.
    206   return ExtractToMemMap(zip_filename, entry_filename, error_msg);
    207 }
    208 
    209 static void SetCloseOnExec(int fd) {
    210   // This dance is more portable than Linux's O_CLOEXEC open(2) flag.
    211   int flags = fcntl(fd, F_GETFD);
    212   if (flags == -1) {
    213     PLOG(WARNING) << "fcntl(" << fd << ", F_GETFD) failed";
    214     return;
    215   }
    216   int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
    217   if (rc == -1) {
    218     PLOG(WARNING) << "fcntl(" << fd << ", F_SETFD, " << flags << ") failed";
    219     return;
    220   }
    221 }
    222 
    223 ZipArchive* ZipArchive::Open(const char* filename, std::string* error_msg) {
    224   DCHECK(filename != nullptr);
    225 
    226   ZipArchiveHandle handle;
    227   const int32_t error = OpenArchive(filename, &handle);
    228   if (error) {
    229     *error_msg = std::string(ErrorCodeString(error));
    230     CloseArchive(handle);
    231     return nullptr;
    232   }
    233 
    234   SetCloseOnExec(GetFileDescriptor(handle));
    235   return new ZipArchive(handle);
    236 }
    237 
    238 ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) {
    239   DCHECK(filename != nullptr);
    240   DCHECK_GT(fd, 0);
    241 
    242   ZipArchiveHandle handle;
    243   const int32_t error = OpenArchiveFd(fd, filename, &handle);
    244   if (error) {
    245     *error_msg = std::string(ErrorCodeString(error));
    246     CloseArchive(handle);
    247     return nullptr;
    248   }
    249 
    250   SetCloseOnExec(GetFileDescriptor(handle));
    251   return new ZipArchive(handle);
    252 }
    253 
    254 ZipEntry* ZipArchive::Find(const char* name, std::string* error_msg) const {
    255   DCHECK(name != nullptr);
    256 
    257   // Resist the urge to delete the space. <: is a bigraph sequence.
    258   std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
    259   const int32_t error = FindEntry(handle_, ZipString(name), zip_entry.get());
    260   if (error) {
    261     *error_msg = std::string(ErrorCodeString(error));
    262     return nullptr;
    263   }
    264 
    265   return new ZipEntry(handle_, zip_entry.release(), name);
    266 }
    267 
    268 ZipArchive::~ZipArchive() {
    269   CloseArchive(handle_);
    270 }
    271 
    272 }  // namespace art
    273