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