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