1 // 2 // Copyright (C) 2018 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 "update_engine/payload_generator/boot_img_filesystem.h" 18 19 #include <base/logging.h> 20 #include <bootimg.h> 21 #include <brillo/secure_blob.h> 22 #include <puffin/utils.h> 23 24 #include "update_engine/common/utils.h" 25 #include "update_engine/payload_generator/delta_diff_generator.h" 26 #include "update_engine/payload_generator/extent_ranges.h" 27 28 using std::string; 29 using std::unique_ptr; 30 using std::vector; 31 32 namespace chromeos_update_engine { 33 34 unique_ptr<BootImgFilesystem> BootImgFilesystem::CreateFromFile( 35 const string& filename) { 36 if (filename.empty()) 37 return nullptr; 38 39 if (brillo::Blob header_magic; 40 !utils::ReadFileChunk(filename, 0, BOOT_MAGIC_SIZE, &header_magic) || 41 memcmp(header_magic.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE) != 0) { 42 return nullptr; 43 } 44 45 // The order of image header fields are different in version 3 from the 46 // previous versions. But the position of "header_version" is fixed at #9 47 // across all image headers. 48 // See details in system/tools/mkbootimg/include/bootimg/bootimg.h 49 constexpr size_t header_version_offset = 50 BOOT_MAGIC_SIZE + 8 * sizeof(uint32_t); 51 brillo::Blob header_version_blob; 52 if (!utils::ReadFileChunk(filename, 53 header_version_offset, 54 sizeof(uint32_t), 55 &header_version_blob)) { 56 return nullptr; 57 } 58 uint32_t header_version = 59 *reinterpret_cast<uint32_t*>(header_version_blob.data()); 60 if (header_version > 3) { 61 LOG(WARNING) << "Boot image header version " << header_version 62 << " isn't supported for parsing"; 63 return nullptr; 64 } 65 66 // Read the bytes of boot image header based on the header version. 67 size_t header_size = 68 header_version == 3 ? sizeof(boot_img_hdr_v3) : sizeof(boot_img_hdr_v0); 69 brillo::Blob header_blob; 70 if (!utils::ReadFileChunk(filename, 0, header_size, &header_blob)) { 71 return nullptr; 72 } 73 74 unique_ptr<BootImgFilesystem> result(new BootImgFilesystem()); 75 result->filename_ = filename; 76 if (header_version < 3) { 77 auto hdr_v0 = reinterpret_cast<boot_img_hdr_v0*>(header_blob.data()); 78 CHECK_EQ(0, memcmp(hdr_v0->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)); 79 CHECK_LT(hdr_v0->header_version, 3u); 80 result->kernel_size_ = hdr_v0->kernel_size; 81 result->ramdisk_size_ = hdr_v0->ramdisk_size; 82 result->page_size_ = hdr_v0->page_size; 83 } else { 84 auto hdr_v3 = reinterpret_cast<boot_img_hdr_v3*>(header_blob.data()); 85 CHECK_EQ(0, memcmp(hdr_v3->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)); 86 CHECK_EQ(3u, hdr_v3->header_version); 87 result->kernel_size_ = hdr_v3->kernel_size; 88 result->ramdisk_size_ = hdr_v3->ramdisk_size; 89 result->page_size_ = 4096; 90 } 91 92 CHECK_GT(result->page_size_, 0u); 93 94 return result; 95 } 96 97 size_t BootImgFilesystem::GetBlockSize() const { 98 // Page size may not be 4K, but we currently only support 4K block size. 99 return kBlockSize; 100 } 101 102 size_t BootImgFilesystem::GetBlockCount() const { 103 return utils::DivRoundUp(utils::FileSize(filename_), kBlockSize); 104 } 105 106 FilesystemInterface::File BootImgFilesystem::GetFile(const string& name, 107 uint64_t offset, 108 uint64_t size) const { 109 File file; 110 file.name = name; 111 file.extents = {ExtentForBytes(kBlockSize, offset, size)}; 112 113 brillo::Blob data; 114 if (utils::ReadFileChunk(filename_, offset, size, &data)) { 115 constexpr size_t kGZipHeaderSize = 10; 116 // Check GZip header magic. 117 if (data.size() > kGZipHeaderSize && data[0] == 0x1F && data[1] == 0x8B) { 118 if (!puffin::LocateDeflatesInGzip(data, &file.deflates)) { 119 LOG(ERROR) << "Error occurred parsing gzip " << name << " at offset " 120 << offset << " of " << filename_ << ", found " 121 << file.deflates.size() << " deflates."; 122 return file; 123 } 124 for (auto& deflate : file.deflates) { 125 deflate.offset += offset * 8; 126 } 127 } 128 } 129 return file; 130 } 131 132 bool BootImgFilesystem::GetFiles(vector<File>* files) const { 133 files->clear(); 134 const uint64_t file_size = utils::FileSize(filename_); 135 // The first page is header. 136 uint64_t offset = page_size_; 137 if (kernel_size_ > 0 && offset + kernel_size_ <= file_size) { 138 files->emplace_back(GetFile("<kernel>", offset, kernel_size_)); 139 } 140 offset += utils::RoundUp(kernel_size_, page_size_); 141 if (ramdisk_size_ > 0 && offset + ramdisk_size_ <= file_size) { 142 files->emplace_back(GetFile("<ramdisk>", offset, ramdisk_size_)); 143 } 144 return true; 145 } 146 147 bool BootImgFilesystem::LoadSettings(brillo::KeyValueStore* store) const { 148 return false; 149 } 150 151 } // namespace chromeos_update_engine 152