| // |
| // Copyright (C) 2018 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "update_engine/payload_generator/boot_img_filesystem.h" |
| |
| #include <base/logging.h> |
| #include <brillo/secure_blob.h> |
| #include <puffin/utils.h> |
| |
| #include "update_engine/common/utils.h" |
| #include "update_engine/payload_generator/delta_diff_generator.h" |
| #include "update_engine/payload_generator/extent_ranges.h" |
| |
| using std::string; |
| using std::unique_ptr; |
| using std::vector; |
| |
| namespace chromeos_update_engine { |
| |
| unique_ptr<BootImgFilesystem> BootImgFilesystem::CreateFromFile( |
| const string& filename) { |
| if (filename.empty()) |
| return nullptr; |
| |
| brillo::Blob header; |
| if (!utils::ReadFileChunk(filename, 0, sizeof(boot_img_hdr), &header) || |
| header.size() != sizeof(boot_img_hdr) || |
| memcmp(header.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE) != 0) { |
| return nullptr; |
| } |
| |
| unique_ptr<BootImgFilesystem> result(new BootImgFilesystem()); |
| result->filename_ = filename; |
| memcpy(&result->hdr_, header.data(), header.size()); |
| return result; |
| } |
| |
| size_t BootImgFilesystem::GetBlockSize() const { |
| // Page size may not be 4K, but we currently only support 4K block size. |
| return kBlockSize; |
| } |
| |
| size_t BootImgFilesystem::GetBlockCount() const { |
| return utils::DivRoundUp(utils::FileSize(filename_), kBlockSize); |
| } |
| |
| FilesystemInterface::File BootImgFilesystem::GetFile(const string& name, |
| uint64_t offset, |
| uint64_t size) const { |
| File file; |
| file.name = name; |
| file.extents = {ExtentForBytes(kBlockSize, offset, size)}; |
| |
| brillo::Blob data; |
| if (utils::ReadFileChunk(filename_, offset, size, &data)) { |
| constexpr size_t kGZipHeaderSize = 10; |
| // Check GZip header magic. |
| if (data.size() > kGZipHeaderSize && data[0] == 0x1F && data[1] == 0x8B) { |
| if (!puffin::LocateDeflatesInGzip(data, &file.deflates)) { |
| // We still use the deflates found even if LocateDeflatesInGzip() fails, |
| // if any deflates are returned, they should be correct, it's possible |
| // something went wrong later but it shouldn't stop us from using the |
| // previous deflates. Another common case is if there's more data after |
| // the gzip, the function will try to parse that as another gzip and |
| // will fail, but we still want the deflates from the first gzip. |
| LOG(WARNING) << "Error occurred parsing gzip " << name << " at offset " |
| << offset << " of " << filename_ << ", found " |
| << file.deflates.size() << " deflates."; |
| } |
| for (auto& deflate : file.deflates) { |
| deflate.offset += offset * 8; |
| } |
| } |
| } |
| return file; |
| } |
| |
| bool BootImgFilesystem::GetFiles(vector<File>* files) const { |
| files->clear(); |
| const uint64_t file_size = utils::FileSize(filename_); |
| // The first page is header. |
| uint64_t offset = hdr_.page_size; |
| if (hdr_.kernel_size > 0 && offset + hdr_.kernel_size <= file_size) { |
| files->emplace_back(GetFile("<kernel>", offset, hdr_.kernel_size)); |
| } |
| offset += utils::RoundUp(hdr_.kernel_size, hdr_.page_size); |
| if (hdr_.ramdisk_size > 0 && offset + hdr_.ramdisk_size <= file_size) { |
| files->emplace_back(GetFile("<ramdisk>", offset, hdr_.ramdisk_size)); |
| } |
| return true; |
| } |
| |
| bool BootImgFilesystem::LoadSettings(brillo::KeyValueStore* store) const { |
| return false; |
| } |
| |
| } // namespace chromeos_update_engine |