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