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 "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