• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <array>
20 
21 #include <android-base/unique_fd.h>
22 #include <android-base/file.h>
23 #include <base/logging.h>
24 #include <bootimg.h>
25 #include <brillo/secure_blob.h>
26 #include <puffin/utils.h>
27 #include <sys/stat.h>
28 
29 #include "update_engine/common/utils.h"
30 #include "update_engine/payload_generator/delta_diff_generator.h"
31 #include "update_engine/payload_generator/extent_ranges.h"
32 
33 using std::string;
34 using std::unique_ptr;
35 using std::vector;
36 
37 namespace chromeos_update_engine {
38 
CreateFromFile(const string & filename)39 unique_ptr<BootImgFilesystem> BootImgFilesystem::CreateFromFile(
40     const string& filename) {
41   if (filename.empty()) {
42     return nullptr;
43   }
44   android::base::unique_fd fd(open(filename.c_str(), O_RDONLY));
45   if (!fd.ok()) {
46     PLOG(ERROR) << "Failed to open " << filename;
47     return nullptr;
48   }
49   struct stat st {};
50   if (fstat(fd.get(), &st) < 0) {
51     return nullptr;
52   }
53   if (static_cast<size_t>(st.st_size) < sizeof(boot_img_hdr_v0)) {
54     LOG(INFO) << "Image " << filename
55               << " is too small to be a boot image. file size: " << st.st_size;
56     return nullptr;
57   }
58   std::array<char, BOOT_MAGIC_SIZE> header_magic{};
59   if (!android::base::ReadFullyAtOffset(
60           fd, header_magic.data(), BOOT_MAGIC_SIZE, 0) ||
61       memcmp(header_magic.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE) != 0) {
62     return nullptr;
63   }
64 
65   // The order of image header fields are different in version 3 from the
66   // previous versions. But the position of "header_version" is fixed at #9
67   // across all image headers.
68   // See details in system/tools/mkbootimg/include/bootimg/bootimg.h
69   constexpr size_t header_version_offset =
70       BOOT_MAGIC_SIZE + 8 * sizeof(uint32_t);
71   std::array<char, sizeof(uint32_t)> header_version_blob{};
72   if (!android::base::ReadFullyAtOffset(fd,
73                                         header_version_blob.data(),
74                                         sizeof(uint32_t),
75                                         header_version_offset)) {
76     return nullptr;
77   }
78   uint32_t header_version =
79       *reinterpret_cast<uint32_t*>(header_version_blob.data());
80   if (header_version > 4) {
81     LOG(WARNING) << "Boot image header version " << header_version
82                  << " isn't supported for parsing";
83     return nullptr;
84   }
85 
86   // Read the bytes of boot image header based on the header version.
87   size_t header_size =
88       header_version == 3 ? sizeof(boot_img_hdr_v3) : sizeof(boot_img_hdr_v0);
89   brillo::Blob header_blob(header_size);
90   if (!ReadFullyAtOffset(fd, header_blob.data(), header_size, 0)) {
91     return nullptr;
92   }
93 
94   unique_ptr<BootImgFilesystem> result(new BootImgFilesystem());
95   result->filename_ = filename;
96   if (header_version < 3) {
97     auto hdr_v0 = reinterpret_cast<boot_img_hdr_v0*>(header_blob.data());
98     CHECK_EQ(0, memcmp(hdr_v0->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE));
99     CHECK_LT(hdr_v0->header_version, 3u);
100     result->kernel_size_ = hdr_v0->kernel_size;
101     result->ramdisk_size_ = hdr_v0->ramdisk_size;
102     result->page_size_ = hdr_v0->page_size;
103   } else if (header_version == 3) {
104     auto hdr_v3 = reinterpret_cast<boot_img_hdr_v3*>(header_blob.data());
105     CHECK_EQ(0, memcmp(hdr_v3->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE));
106     CHECK_EQ(3u, hdr_v3->header_version);
107     result->kernel_size_ = hdr_v3->kernel_size;
108     result->ramdisk_size_ = hdr_v3->ramdisk_size;
109     result->page_size_ = 4096;
110   } else {
111     auto hdr_v4 = reinterpret_cast<boot_img_hdr_v4*>(header_blob.data());
112     CHECK_EQ(0, memcmp(hdr_v4->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE));
113     CHECK_EQ(hdr_v4->header_version, 4u);
114     result->kernel_size_ = hdr_v4->kernel_size;
115     result->ramdisk_size_ = hdr_v4->ramdisk_size;
116     // In boot image v3 and v4, page size is hard coded as 4096
117     result->page_size_ = 4096;
118     result->signature_size_ = hdr_v4->signature_size;
119   }
120 
121   CHECK_GT(result->page_size_, 0u);
122 
123   return result;
124 }
125 
GetBlockSize() const126 size_t BootImgFilesystem::GetBlockSize() const {
127   // Page size may not be 4K, but we currently only support 4K block size.
128   return kBlockSize;
129 }
130 
GetBlockCount() const131 size_t BootImgFilesystem::GetBlockCount() const {
132   return utils::DivRoundUp(utils::FileSize(filename_), kBlockSize);
133 }
134 
GetFile(const string & name,uint64_t offset,uint64_t size) const135 FilesystemInterface::File BootImgFilesystem::GetFile(const string& name,
136                                                      uint64_t offset,
137                                                      uint64_t size) const {
138   File file;
139   file.name = name;
140   file.extents = {ExtentForBytes(kBlockSize, offset, size)};
141 
142   brillo::Blob data;
143   if (utils::ReadFileChunk(filename_, offset, size, &data) &&
144       data.size() == size) {
145     constexpr size_t kGZipHeaderSize = 10;
146     // Check GZip header magic.
147     if (data.size() > kGZipHeaderSize && data[0] == 0x1F && data[1] == 0x8B) {
148       if (!puffin::LocateDeflatesInGzip(data, &file.deflates)) {
149         LOG(ERROR) << "Error occurred parsing gzip " << name << " at offset "
150                    << offset << " of " << filename_ << ", found "
151                    << file.deflates.size() << " deflates.";
152         return file;
153       }
154       for (auto& deflate : file.deflates) {
155         deflate.offset += offset * 8;
156       }
157     }
158   }
159   return file;
160 }
161 
GetFiles(vector<File> * files) const162 bool BootImgFilesystem::GetFiles(vector<File>* files) const {
163   files->clear();
164   const uint64_t file_size = utils::FileSize(filename_);
165   // The first page is header.
166   uint64_t offset = page_size_;
167   if (kernel_size_ > 0 && offset + kernel_size_ <= file_size) {
168     files->emplace_back(GetFile("<kernel>", offset, kernel_size_));
169   }
170   offset += utils::RoundUp(kernel_size_, page_size_);
171   if (ramdisk_size_ > 0 && offset + ramdisk_size_ <= file_size) {
172     files->emplace_back(GetFile("<ramdisk>", offset, ramdisk_size_));
173   }
174   offset += utils::RoundUp(ramdisk_size_, page_size_);
175   if (signature_size_ > 0) {
176     files->emplace_back(GetFile("<boot signature>", offset, signature_size_));
177   }
178   return true;
179 }
180 
181 }  // namespace chromeos_update_engine
182