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