Home | History | Annotate | Download | only in format
      1 /*
      2  * Copyright (C) 2017 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 "format/Container.h"
     18 
     19 #include "android-base/scopeguard.h"
     20 #include "android-base/stringprintf.h"
     21 
     22 #include "trace/TraceBuffer.h"
     23 
     24 using ::android::base::StringPrintf;
     25 using ::google::protobuf::io::CodedInputStream;
     26 using ::google::protobuf::io::CodedOutputStream;
     27 using ::google::protobuf::io::ZeroCopyOutputStream;
     28 
     29 namespace aapt {
     30 
     31 constexpr const static uint32_t kContainerFormatMagic = 0x54504141u;
     32 constexpr const static uint32_t kContainerFormatVersion = 1u;
     33 
     34 ContainerWriter::ContainerWriter(ZeroCopyOutputStream* out, size_t entry_count)
     35     : out_(out), total_entry_count_(entry_count), current_entry_count_(0u) {
     36   CodedOutputStream coded_out(out_);
     37 
     38   // Write the magic.
     39   coded_out.WriteLittleEndian32(kContainerFormatMagic);
     40 
     41   // Write the version.
     42   coded_out.WriteLittleEndian32(kContainerFormatVersion);
     43 
     44   // Write the total number of entries.
     45   coded_out.WriteLittleEndian32(static_cast<uint32_t>(total_entry_count_));
     46 
     47   if (coded_out.HadError()) {
     48     error_ = "failed writing container format header";
     49   }
     50 }
     51 
     52 inline static void WritePadding(int padding, CodedOutputStream* out) {
     53   if (padding < 4) {
     54     const uint32_t zero = 0u;
     55     out->WriteRaw(&zero, padding);
     56   }
     57 }
     58 
     59 bool ContainerWriter::AddResTableEntry(const pb::ResourceTable& table) {
     60   if (current_entry_count_ >= total_entry_count_) {
     61     error_ = "too many entries being serialized";
     62     return false;
     63   }
     64   current_entry_count_++;
     65 
     66   CodedOutputStream coded_out(out_);
     67 
     68   // Write the type.
     69   coded_out.WriteLittleEndian32(kResTable);
     70 
     71   // Write the aligned size.
     72   const ::google::protobuf::uint64 size = table.ByteSize();
     73   const int padding = 4 - (size % 4);
     74   coded_out.WriteLittleEndian64(size);
     75 
     76   // Write the table.
     77   table.SerializeWithCachedSizes(&coded_out);
     78 
     79   // Write the padding.
     80   WritePadding(padding, &coded_out);
     81 
     82   if (coded_out.HadError()) {
     83     error_ = "failed writing to output";
     84     return false;
     85   }
     86   return true;
     87 }
     88 
     89 bool ContainerWriter::AddResFileEntry(const pb::internal::CompiledFile& file,
     90                                       io::KnownSizeInputStream* in) {
     91   if (current_entry_count_ >= total_entry_count_) {
     92     error_ = "too many entries being serialized";
     93     return false;
     94   }
     95   current_entry_count_++;
     96 
     97   constexpr const static int kResFileEntryHeaderSize = 12;
     98 
     99   CodedOutputStream coded_out(out_);
    100 
    101   // Write the type.
    102   coded_out.WriteLittleEndian32(kResFile);
    103 
    104   // Write the aligned size.
    105   const ::google::protobuf::uint32 header_size = file.ByteSize();
    106   const int header_padding = 4 - (header_size % 4);
    107   const ::google::protobuf::uint64 data_size = in->TotalSize();
    108   const int data_padding = 4 - (data_size % 4);
    109   coded_out.WriteLittleEndian64(kResFileEntryHeaderSize + header_size + header_padding + data_size +
    110                                 data_padding);
    111 
    112   // Write the res file header size.
    113   coded_out.WriteLittleEndian32(header_size);
    114 
    115   // Write the data payload size.
    116   coded_out.WriteLittleEndian64(data_size);
    117 
    118   // Write the header.
    119   file.SerializeToCodedStream(&coded_out);
    120 
    121   WritePadding(header_padding, &coded_out);
    122 
    123   // Write the data payload. We need to call Trim() since we are going to write to the underlying
    124   // ZeroCopyOutputStream.
    125   coded_out.Trim();
    126 
    127   // Check at this point if there were any errors.
    128   if (coded_out.HadError()) {
    129     error_ = "failed writing to output";
    130     return false;
    131   }
    132 
    133   if (!io::Copy(out_, in)) {
    134     if (in->HadError()) {
    135       std::ostringstream error;
    136       error << "failed reading from input: " << in->GetError();
    137       error_ = error.str();
    138     } else {
    139       error_ = "failed writing to output";
    140     }
    141     return false;
    142   }
    143   WritePadding(data_padding, &coded_out);
    144 
    145   if (coded_out.HadError()) {
    146     error_ = "failed writing to output";
    147     return false;
    148   }
    149   return true;
    150 }
    151 
    152 bool ContainerWriter::HadError() const {
    153   return !error_.empty();
    154 }
    155 
    156 std::string ContainerWriter::GetError() const {
    157   return error_;
    158 }
    159 
    160 static bool AlignRead(CodedInputStream* in) {
    161   const int padding = 4 - (in->CurrentPosition() % 4);
    162   if (padding < 4) {
    163     return in->Skip(padding);
    164   }
    165   return true;
    166 }
    167 
    168 ContainerReaderEntry::ContainerReaderEntry(ContainerReader* reader) : reader_(reader) {
    169 }
    170 
    171 ContainerEntryType ContainerReaderEntry::Type() const {
    172   return type_;
    173 }
    174 
    175 bool ContainerReaderEntry::GetResTable(pb::ResourceTable* out_table) {
    176   TRACE_CALL();
    177   CHECK(type_ == ContainerEntryType::kResTable) << "reading a kResTable when the type is kResFile";
    178   if (length_ > std::numeric_limits<int>::max()) {
    179     reader_->error_ = StringPrintf("entry length %zu is too large", length_);
    180     return false;
    181   }
    182 
    183   CodedInputStream& coded_in = reader_->coded_in_;
    184 
    185   const CodedInputStream::Limit limit = coded_in.PushLimit(static_cast<int>(length_));
    186   auto guard = ::android::base::make_scope_guard([&]() { coded_in.PopLimit(limit); });
    187 
    188   if (!out_table->ParseFromCodedStream(&coded_in)) {
    189     reader_->error_ = "failed to parse ResourceTable";
    190     return false;
    191   }
    192   return true;
    193 }
    194 
    195 bool ContainerReaderEntry::GetResFileOffsets(pb::internal::CompiledFile* out_file,
    196                                              off64_t* out_offset, size_t* out_len) {
    197   CHECK(type_ == ContainerEntryType::kResFile) << "reading a kResFile when the type is kResTable";
    198 
    199   CodedInputStream& coded_in = reader_->coded_in_;
    200 
    201   // Read the ResFile header.
    202   ::google::protobuf::uint32 header_length;
    203   if (!coded_in.ReadLittleEndian32(&header_length)) {
    204     std::ostringstream error;
    205     error << "failed to read header length from input: " << reader_->in_->GetError();
    206     reader_->error_ = error.str();
    207     return false;
    208   }
    209 
    210   ::google::protobuf::uint64 data_length;
    211   if (!coded_in.ReadLittleEndian64(&data_length)) {
    212     std::ostringstream error;
    213     error << "failed to read data length from input: " << reader_->in_->GetError();
    214     reader_->error_ = error.str();
    215     return false;
    216   }
    217 
    218   if (header_length > std::numeric_limits<int>::max()) {
    219     std::ostringstream error;
    220     error << "header length " << header_length << " is too large";
    221     reader_->error_ = error.str();
    222     return false;
    223   }
    224 
    225   if (data_length > std::numeric_limits<size_t>::max()) {
    226     std::ostringstream error;
    227     error << "data length " << data_length << " is too large";
    228     reader_->error_ = error.str();
    229     return false;
    230   }
    231 
    232   {
    233     const CodedInputStream::Limit limit = coded_in.PushLimit(static_cast<int>(header_length));
    234     auto guard = ::android::base::make_scope_guard([&]() { coded_in.PopLimit(limit); });
    235 
    236     if (!out_file->ParseFromCodedStream(&coded_in)) {
    237       reader_->error_ = "failed to parse CompiledFile header";
    238       return false;
    239     }
    240   }
    241 
    242   AlignRead(&coded_in);
    243 
    244   *out_offset = coded_in.CurrentPosition();
    245   *out_len = data_length;
    246 
    247   coded_in.Skip(static_cast<int>(data_length));
    248   AlignRead(&coded_in);
    249   return true;
    250 }
    251 
    252 bool ContainerReaderEntry::HadError() const {
    253   return reader_->HadError();
    254 }
    255 
    256 std::string ContainerReaderEntry::GetError() const {
    257   return reader_->GetError();
    258 }
    259 
    260 ContainerReader::ContainerReader(io::InputStream* in)
    261     : in_(in),
    262       adaptor_(in),
    263       coded_in_(&adaptor_),
    264       total_entry_count_(0u),
    265       current_entry_count_(0u),
    266       entry_(this) {
    267   TRACE_CALL();
    268   ::google::protobuf::uint32 magic;
    269   if (!coded_in_.ReadLittleEndian32(&magic)) {
    270     std::ostringstream error;
    271     error << "failed to read magic from input: " << in_->GetError();
    272     error_ = error.str();
    273     return;
    274   }
    275 
    276   if (magic != kContainerFormatMagic) {
    277     error_ =
    278         StringPrintf("magic value is 0x%08x but AAPT expects 0x%08x", magic, kContainerFormatMagic);
    279     return;
    280   }
    281 
    282   ::google::protobuf::uint32 version;
    283   if (!coded_in_.ReadLittleEndian32(&version)) {
    284     std::ostringstream error;
    285     error << "failed to read version from input: " << in_->GetError();
    286     error_ = error.str();
    287     return;
    288   }
    289 
    290   if (version != kContainerFormatVersion) {
    291     error_ = StringPrintf("container version is 0x%08x but AAPT expects version 0x%08x", version,
    292                           kContainerFormatVersion);
    293     return;
    294   }
    295 
    296   ::google::protobuf::uint32 total_entry_count;
    297   if (!coded_in_.ReadLittleEndian32(&total_entry_count)) {
    298     std::ostringstream error;
    299     error << "failed to read entry count from input: " << in_->GetError();
    300     error_ = error.str();
    301     return;
    302   }
    303 
    304   total_entry_count_ = total_entry_count;
    305 }
    306 
    307 ContainerReaderEntry* ContainerReader::Next() {
    308   if (current_entry_count_ >= total_entry_count_) {
    309     return nullptr;
    310   }
    311   current_entry_count_++;
    312 
    313   // Ensure the next read is aligned.
    314   AlignRead(&coded_in_);
    315 
    316   ::google::protobuf::uint32 entry_type;
    317   if (!coded_in_.ReadLittleEndian32(&entry_type)) {
    318     std::ostringstream error;
    319     error << "failed reading entry type from input: " << in_->GetError();
    320     error_ = error.str();
    321     return nullptr;
    322   }
    323 
    324   ::google::protobuf::uint64 entry_length;
    325   if (!coded_in_.ReadLittleEndian64(&entry_length)) {
    326     std::ostringstream error;
    327     error << "failed reading entry length from input: " << in_->GetError();
    328     error_ = error.str();
    329     return nullptr;
    330   }
    331 
    332   if (entry_type == ContainerEntryType::kResFile || entry_type == ContainerEntryType::kResTable) {
    333     entry_.type_ = static_cast<ContainerEntryType>(entry_type);
    334   } else {
    335     error_ = StringPrintf("entry type 0x%08x is invalid", entry_type);
    336     return nullptr;
    337   }
    338 
    339   if (entry_length > std::numeric_limits<size_t>::max()) {
    340     std::ostringstream error;
    341     error << "entry length " << entry_length << " is too large";
    342     error_ = error.str();
    343     return nullptr;
    344   }
    345 
    346   entry_.length_ = entry_length;
    347   return &entry_;
    348 }
    349 
    350 bool ContainerReader::HadError() const {
    351   return !error_.empty();
    352 }
    353 
    354 std::string ContainerReader::GetError() const {
    355   return error_;
    356 }
    357 
    358 }  // namespace aapt
    359