Home | History | Annotate | Download | only in flatten
      1 /*
      2  * Copyright (C) 2015 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 "flatten/Archive.h"
     18 
     19 #include <cstdio>
     20 #include <memory>
     21 #include <string>
     22 #include <vector>
     23 
     24 #include "android-base/errors.h"
     25 #include "android-base/macros.h"
     26 #include "androidfw/StringPiece.h"
     27 #include "ziparchive/zip_writer.h"
     28 
     29 #include "util/Files.h"
     30 
     31 using android::StringPiece;
     32 
     33 namespace aapt {
     34 
     35 namespace {
     36 
     37 class DirectoryWriter : public IArchiveWriter {
     38  public:
     39   DirectoryWriter() = default;
     40 
     41   bool Open(const StringPiece& out_dir) {
     42     dir_ = out_dir.to_string();
     43     file::FileType type = file::GetFileType(dir_);
     44     if (type == file::FileType::kNonexistant) {
     45       error_ = "directory does not exist";
     46       return false;
     47     } else if (type != file::FileType::kDirectory) {
     48       error_ = "not a directory";
     49       return false;
     50     }
     51     return true;
     52   }
     53 
     54   bool StartEntry(const StringPiece& path, uint32_t flags) override {
     55     if (file_) {
     56       return false;
     57     }
     58 
     59     std::string full_path = dir_;
     60     file::AppendPath(&full_path, path);
     61     file::mkdirs(file::GetStem(full_path));
     62 
     63     file_ = {fopen(full_path.data(), "wb"), fclose};
     64     if (!file_) {
     65       error_ = android::base::SystemErrorCodeToString(errno);
     66       return false;
     67     }
     68     return true;
     69   }
     70 
     71   bool Write(const void* data, int len) override {
     72     if (!file_) {
     73       return false;
     74     }
     75 
     76     if (fwrite(data, 1, len, file_.get()) != static_cast<size_t>(len)) {
     77       error_ = android::base::SystemErrorCodeToString(errno);
     78       file_.reset(nullptr);
     79       return false;
     80     }
     81     return true;
     82   }
     83 
     84   bool FinishEntry() override {
     85     if (!file_) {
     86       return false;
     87     }
     88     file_.reset(nullptr);
     89     return true;
     90   }
     91 
     92   bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
     93     if (!StartEntry(path, flags)) {
     94       return false;
     95     }
     96 
     97     const void* data = nullptr;
     98     size_t len = 0;
     99     while (in->Next(&data, &len)) {
    100       if (!Write(data, static_cast<int>(len))) {
    101         return false;
    102       }
    103     }
    104     return !in->HadError();
    105   }
    106 
    107   bool HadError() const override { return !error_.empty(); }
    108 
    109   std::string GetError() const override { return error_; }
    110 
    111  private:
    112   DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
    113 
    114   std::string dir_;
    115   std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
    116   std::string error_;
    117 };
    118 
    119 class ZipFileWriter : public IArchiveWriter {
    120  public:
    121   ZipFileWriter() = default;
    122 
    123   bool Open(const StringPiece& path) {
    124     file_ = {fopen(path.data(), "w+b"), fclose};
    125     if (!file_) {
    126       error_ = android::base::SystemErrorCodeToString(errno);
    127       return false;
    128     }
    129     writer_ = util::make_unique<ZipWriter>(file_.get());
    130     return true;
    131   }
    132 
    133   bool StartEntry(const StringPiece& path, uint32_t flags) override {
    134     if (!writer_) {
    135       return false;
    136     }
    137 
    138     size_t zip_flags = 0;
    139     if (flags & ArchiveEntry::kCompress) {
    140       zip_flags |= ZipWriter::kCompress;
    141     }
    142 
    143     if (flags & ArchiveEntry::kAlign) {
    144       zip_flags |= ZipWriter::kAlign32;
    145     }
    146 
    147     int32_t result = writer_->StartEntry(path.data(), zip_flags);
    148     if (result != 0) {
    149       error_ = ZipWriter::ErrorCodeString(result);
    150       return false;
    151     }
    152     return true;
    153   }
    154 
    155   bool Write(const void* data, int len) override {
    156     int32_t result = writer_->WriteBytes(data, len);
    157     if (result != 0) {
    158       error_ = ZipWriter::ErrorCodeString(result);
    159       return false;
    160     }
    161     return true;
    162   }
    163 
    164   bool FinishEntry() override {
    165     int32_t result = writer_->FinishEntry();
    166     if (result != 0) {
    167       error_ = ZipWriter::ErrorCodeString(result);
    168       return false;
    169     }
    170     return true;
    171   }
    172 
    173   bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
    174     while (true) {
    175       if (!StartEntry(path, flags)) {
    176         return false;
    177       }
    178 
    179       const void* data = nullptr;
    180       size_t len = 0;
    181       while (in->Next(&data, &len)) {
    182         if (!Write(data, static_cast<int>(len))) {
    183           return false;
    184         }
    185       }
    186 
    187       if (in->HadError()) {
    188         return false;
    189       }
    190 
    191       if (!FinishEntry()) {
    192         return false;
    193       }
    194 
    195       // Check to see if the file was compressed enough. This is preserving behavior of AAPT.
    196       if ((flags & ArchiveEntry::kCompress) != 0 && in->CanRewind()) {
    197         ZipWriter::FileEntry last_entry;
    198         int32_t result = writer_->GetLastEntry(&last_entry);
    199         CHECK(result == 0);
    200         if (last_entry.compressed_size + (last_entry.compressed_size / 10) >
    201             last_entry.uncompressed_size) {
    202           // The file was not compressed enough, rewind and store it uncompressed.
    203           if (!in->Rewind()) {
    204             // Well we tried, may as well keep what we had.
    205             return true;
    206           }
    207 
    208           int32_t result = writer_->DiscardLastEntry();
    209           if (result != 0) {
    210             error_ = ZipWriter::ErrorCodeString(result);
    211             return false;
    212           }
    213           flags &= ~ArchiveEntry::kCompress;
    214 
    215           continue;
    216         }
    217       }
    218       return true;
    219     }
    220   }
    221 
    222   bool HadError() const override { return !error_.empty(); }
    223 
    224   std::string GetError() const override { return error_; }
    225 
    226   virtual ~ZipFileWriter() {
    227     if (writer_) {
    228       writer_->Finish();
    229     }
    230   }
    231 
    232  private:
    233   DISALLOW_COPY_AND_ASSIGN(ZipFileWriter);
    234 
    235   std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
    236   std::unique_ptr<ZipWriter> writer_;
    237   std::string error_;
    238 };
    239 
    240 }  // namespace
    241 
    242 std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(IDiagnostics* diag,
    243                                                              const StringPiece& path) {
    244   std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
    245   if (!writer->Open(path)) {
    246     diag->Error(DiagMessage(path) << writer->GetError());
    247     return {};
    248   }
    249   return std::move(writer);
    250 }
    251 
    252 std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
    253                                                            const StringPiece& path) {
    254   std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
    255   if (!writer->Open(path)) {
    256     diag->Error(DiagMessage(path) << writer->GetError());
    257     return {};
    258   }
    259   return std::move(writer);
    260 }
    261 
    262 }  // namespace aapt
    263