• 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 
ContainerWriter(ZeroCopyOutputStream * out,size_t entry_count)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 
WritePadding(int padding,CodedOutputStream * out)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 
AddResTableEntry(const pb::ResourceTable & table)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 
AddResFileEntry(const pb::internal::CompiledFile & file,io::KnownSizeInputStream * in)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 
HadError() const152 bool ContainerWriter::HadError() const {
153   return !error_.empty();
154 }
155 
GetError() const156 std::string ContainerWriter::GetError() const {
157   return error_;
158 }
159 
AlignRead(CodedInputStream * in)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 
ContainerReaderEntry(ContainerReader * reader)168 ContainerReaderEntry::ContainerReaderEntry(ContainerReader* reader) : reader_(reader) {
169 }
170 
Type() const171 ContainerEntryType ContainerReaderEntry::Type() const {
172   return type_;
173 }
174 
GetResTable(pb::ResourceTable * out_table)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 
GetResFileOffsets(pb::internal::CompiledFile * out_file,off64_t * out_offset,size_t * out_len)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 
HadError() const252 bool ContainerReaderEntry::HadError() const {
253   return reader_->HadError();
254 }
255 
GetError() const256 std::string ContainerReaderEntry::GetError() const {
257   return reader_->GetError();
258 }
259 
ContainerReader(io::InputStream * in)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 
Next()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 
HadError() const350 bool ContainerReader::HadError() const {
351   return !error_.empty();
352 }
353 
GetError() const354 std::string ContainerReader::GetError() const {
355   return error_;
356 }
357 
358 }  // namespace aapt
359