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