1 /*
2 * Copyright (C) 2021 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 "vendor_boot_img_utils.h"
18
19 #include <string.h>
20
21 #include <android-base/file.h>
22 #include <android-base/result.h>
23 #include <bootimg.h>
24 #include <libavb/libavb.h>
25
26 namespace {
27
28 using android::base::Result;
29
30 // Updates a given buffer by creating a new one.
31 class DataUpdater {
32 public:
DataUpdater(const std::string & old_data)33 DataUpdater(const std::string& old_data) : old_data_(&old_data) {
34 old_data_ptr_ = old_data_->data();
35 new_data_.resize(old_data_->size(), '\0');
36 new_data_ptr_ = new_data_.data();
37 }
38 // Copy |num_bytes| from src to dst.
Copy(uint32_t num_bytes)39 [[nodiscard]] Result<void> Copy(uint32_t num_bytes) {
40 if (num_bytes == 0) return {};
41 if (auto res = CheckAdvance(old_data_ptr_, old_end(), num_bytes, __FUNCTION__); !res.ok())
42 return res;
43 if (auto res = CheckAdvance(new_data_ptr_, new_end(), num_bytes, __FUNCTION__); !res.ok())
44 return res;
45 memcpy(new_data_ptr_, old_data_ptr_, num_bytes);
46 old_data_ptr_ += num_bytes;
47 new_data_ptr_ += num_bytes;
48 return {};
49 }
50 // Replace |old_num_bytes| from src with new data.
Replace(uint32_t old_num_bytes,const std::string & new_data)51 [[nodiscard]] Result<void> Replace(uint32_t old_num_bytes, const std::string& new_data) {
52 return Replace(old_num_bytes, new_data.data(), new_data.size());
53 }
Replace(uint32_t old_num_bytes,const void * new_data,uint32_t new_data_size)54 [[nodiscard]] Result<void> Replace(uint32_t old_num_bytes, const void* new_data,
55 uint32_t new_data_size) {
56 if (auto res = CheckAdvance(old_data_ptr_, old_end(), old_num_bytes, __FUNCTION__);
57 !res.ok())
58 return res;
59 old_data_ptr_ += old_num_bytes;
60
61 if (new_data_size == 0) return {};
62 if (auto res = CheckAdvance(new_data_ptr_, new_end(), new_data_size, __FUNCTION__);
63 !res.ok())
64 return res;
65 memcpy(new_data_ptr_, new_data, new_data_size);
66 new_data_ptr_ += new_data_size;
67 return {};
68 }
69 // Skip |old_skip| from src and |new_skip| from dst, respectively.
Skip(uint32_t old_skip,uint32_t new_skip)70 [[nodiscard]] Result<void> Skip(uint32_t old_skip, uint32_t new_skip) {
71 if (auto res = CheckAdvance(old_data_ptr_, old_end(), old_skip, __FUNCTION__); !res.ok())
72 return res;
73 old_data_ptr_ += old_skip;
74 if (auto res = CheckAdvance(new_data_ptr_, new_end(), new_skip, __FUNCTION__); !res.ok())
75 return res;
76 new_data_ptr_ += new_skip;
77 return {};
78 }
79
Seek(uint32_t offset)80 [[nodiscard]] Result<void> Seek(uint32_t offset) {
81 if (offset > size()) return Errorf("Cannot seek 0x{:x}, size is 0x{:x}", offset, size());
82 old_data_ptr_ = old_begin() + offset;
83 new_data_ptr_ = new_begin() + offset;
84 return {};
85 }
86
Finish()87 std::string Finish() {
88 new_data_ptr_ = nullptr;
89 return std::move(new_data_);
90 }
91
CheckOffset(uint32_t old_offset,uint32_t new_offset)92 [[nodiscard]] Result<void> CheckOffset(uint32_t old_offset, uint32_t new_offset) {
93 if (old_begin() + old_offset != old_cur())
94 return Errorf("Old offset mismatch: expected: 0x{:x}, actual: 0x{:x}", old_offset,
95 old_cur() - old_begin());
96 if (new_begin() + new_offset != new_cur())
97 return Errorf("New offset mismatch: expected: 0x{:x}, actual: 0x{:x}", new_offset,
98 new_cur() - new_begin());
99 return {};
100 }
101
size() const102 uint64_t size() const { return old_data_->size(); }
old_begin() const103 const char* old_begin() const { return old_data_->data(); }
old_cur()104 const char* old_cur() { return old_data_ptr_; }
old_end() const105 const char* old_end() const { return old_data_->data() + old_data_->size(); }
new_begin()106 char* new_begin() { return new_data_.data(); }
new_cur()107 char* new_cur() { return new_data_ptr_; }
new_end()108 char* new_end() { return new_data_.data() + new_data_.size(); }
109
110 private:
111 // Check if it is okay to advance |num_bytes| from |current|.
CheckAdvance(const char * current,const char * end,uint32_t num_bytes,const char * op)112 [[nodiscard]] Result<void> CheckAdvance(const char* current, const char* end,
113 uint32_t num_bytes, const char* op) {
114 auto new_end = current + num_bytes;
115 if (new_end < current /* add overflow */)
116 return Errorf("{}: Addition overflow: 0x{} + 0x{:x} < 0x{}", op, fmt::ptr(current),
117 num_bytes, fmt::ptr(current));
118 if (new_end > end)
119 return Errorf("{}: Boundary overflow: 0x{} + 0x{:x} > 0x{}", op, fmt::ptr(current),
120 num_bytes, fmt::ptr(end));
121 return {};
122 }
123 const std::string* old_data_;
124 std::string new_data_;
125 const char* old_data_ptr_;
126 char* new_data_ptr_;
127 };
128
129 // Get the size of vendor boot header.
get_vendor_boot_header_size(const vendor_boot_img_hdr_v3 * hdr)130 [[nodiscard]] Result<uint32_t> get_vendor_boot_header_size(const vendor_boot_img_hdr_v3* hdr) {
131 if (hdr->header_version == 3) return sizeof(vendor_boot_img_hdr_v3);
132 if (hdr->header_version == 4) return sizeof(vendor_boot_img_hdr_v4);
133 return Errorf("Unrecognized vendor boot header version {}", hdr->header_version);
134 }
135
136 // Check that content contains a valid vendor boot image header with a version at least |version|.
check_vendor_boot_hdr(const std::string & content,uint32_t version)137 [[nodiscard]] Result<void> check_vendor_boot_hdr(const std::string& content, uint32_t version) {
138 // get_vendor_boot_header_size reads header_version, so make sure reading it does not
139 // go out of bounds by ensuring that the content has at least the size of V3 header.
140 if (content.size() < sizeof(vendor_boot_img_hdr_v3)) {
141 return Errorf("Size of vendor boot is 0x{:x}, less than size of V3 header: 0x{:x}",
142 content.size(), sizeof(vendor_boot_img_hdr_v3));
143 }
144 // Now read hdr->header_version and assert the size.
145 auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(content.data());
146 auto expect_header_size = get_vendor_boot_header_size(hdr);
147 if (!expect_header_size.ok()) return expect_header_size.error();
148 if (content.size() < *expect_header_size) {
149 return Errorf("Size of vendor boot is 0x{:x}, less than size of V{} header: 0x{:x}",
150 content.size(), version, *expect_header_size);
151 }
152 if (memcmp(hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE) != 0) {
153 return Errorf("Vendor boot image magic mismatch");
154 }
155 if (hdr->page_size == 0) {
156 return Errorf("Page size cannot be zero");
157 }
158 if (hdr->header_version < version) {
159 return Errorf("Require vendor boot header V{} but is V{}", version, hdr->header_version);
160 }
161 return {};
162 }
163
164 // Wrapper of ReadFdToString. Seek to the beginning and read the whole file to string.
load_file(android::base::borrowed_fd fd,uint64_t expected_size,const char * what)165 [[nodiscard]] Result<std::string> load_file(android::base::borrowed_fd fd, uint64_t expected_size,
166 const char* what) {
167 if (lseek(fd.get(), 0, SEEK_SET) != 0) {
168 return ErrnoErrorf("Can't seek to the beginning of {} image", what);
169 }
170 std::string content;
171 if (!android::base::ReadFdToString(fd, &content)) {
172 return ErrnoErrorf("Cannot read {} to string", what);
173 }
174 if (content.size() != expected_size) {
175 return Errorf("Size of {} does not match, expected 0x{:x}, read 0x{:x}", what,
176 expected_size, content.size());
177 }
178 return content;
179 }
180
181 // Wrapper of WriteStringToFd. Seek to the beginning and write the whole file to string.
store_file(android::base::borrowed_fd fd,const std::string & data,const char * what)182 [[nodiscard]] Result<void> store_file(android::base::borrowed_fd fd, const std::string& data,
183 const char* what) {
184 if (lseek(fd.get(), 0, SEEK_SET) != 0) {
185 return ErrnoErrorf("Cannot seek to beginning of {} before writing", what);
186 }
187 if (!android::base::WriteStringToFd(data, fd)) {
188 return ErrnoErrorf("Cannot write new content to {}", what);
189 }
190 if (TEMP_FAILURE_RETRY(ftruncate(fd.get(), data.size())) == -1) {
191 return ErrnoErrorf("Truncating new vendor boot image to 0x{:x} fails", data.size());
192 }
193 return {};
194 }
195
196 // Copy AVB footer if it exists in the old buffer.
copy_avb_footer(DataUpdater * updater)197 [[nodiscard]] Result<void> copy_avb_footer(DataUpdater* updater) {
198 if (updater->size() < AVB_FOOTER_SIZE) return {};
199 if (auto res = updater->Seek(updater->size() - AVB_FOOTER_SIZE); !res.ok()) return res;
200 if (memcmp(updater->old_cur(), AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) return {};
201 return updater->Copy(AVB_FOOTER_SIZE);
202 }
203
204 // round |value| up to a multiple of |page_size|.
205 // aware that this can be integer overflow if value is too large
round_up(uint32_t value,uint32_t page_size)206 inline uint32_t round_up(uint32_t value, uint32_t page_size) {
207 return (value + page_size - 1) / page_size * page_size;
208 }
209
210 // Replace the vendor ramdisk as a whole.
replace_default_vendor_ramdisk(const std::string & vendor_boot,const std::string & new_ramdisk,const std::string & new_dtb)211 [[nodiscard]] Result<std::string> replace_default_vendor_ramdisk(const std::string& vendor_boot,
212 const std::string& new_ramdisk,
213 const std::string& new_dtb) {
214 if (auto res = check_vendor_boot_hdr(vendor_boot, 3); !res.ok()) return res.error();
215 auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(vendor_boot.data());
216 auto hdr_size = get_vendor_boot_header_size(hdr);
217 if (!hdr_size.ok()) return hdr_size.error();
218 // Refer to bootimg.h for details. Numbers are in bytes.
219 const uint32_t o = round_up(*hdr_size, hdr->page_size);
220 const uint32_t p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);
221 const uint32_t q = round_up(hdr->dtb_size, hdr->page_size);
222
223 DataUpdater updater(vendor_boot);
224
225 // Copy header (O bytes), then update fields in header.
226 if (auto res = updater.Copy(o); !res.ok()) return res.error();
227 auto new_hdr = reinterpret_cast<vendor_boot_img_hdr_v3*>(updater.new_begin());
228 new_hdr->vendor_ramdisk_size = new_ramdisk.size();
229 // Because it is unknown how the new ramdisk is fragmented, the whole table is replaced
230 // with a single entry representing the full ramdisk.
231 if (new_hdr->header_version >= 4) {
232 auto new_hdr_v4 = static_cast<vendor_boot_img_hdr_v4*>(new_hdr);
233 new_hdr_v4->vendor_ramdisk_table_entry_size = sizeof(vendor_ramdisk_table_entry_v4);
234 new_hdr_v4->vendor_ramdisk_table_entry_num = 1;
235 new_hdr_v4->vendor_ramdisk_table_size = new_hdr_v4->vendor_ramdisk_table_entry_num *
236 new_hdr_v4->vendor_ramdisk_table_entry_size;
237 }
238
239 // Copy the new ramdisk.
240 if (auto res = updater.Replace(hdr->vendor_ramdisk_size, new_ramdisk); !res.ok())
241 return res.error();
242 const uint32_t new_p = round_up(new_hdr->vendor_ramdisk_size, new_hdr->page_size);
243 if (auto res = updater.Skip(p - hdr->vendor_ramdisk_size, new_p - new_hdr->vendor_ramdisk_size);
244 !res.ok())
245 return res.error();
246 if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error();
247
248 // Copy DTB (Q bytes). Replace if a new one was provided.
249 new_hdr->dtb_size = !new_dtb.empty() ? new_dtb.size() : hdr->dtb_size;
250 const uint32_t new_q = round_up(new_hdr->dtb_size, new_hdr->page_size);
251 if (new_dtb.empty()) {
252 if (auto res = updater.Copy(q); !res.ok()) return res.error();
253 } else {
254 if (auto res = updater.Replace(hdr->dtb_size, new_dtb); !res.ok()) return res.error();
255 if (auto res = updater.Skip(q - hdr->dtb_size, new_q - new_hdr->dtb_size); !res.ok())
256 return res.error();
257 }
258 if (auto res = updater.CheckOffset(o + p + q, o + new_p + new_q); !res.ok()) {
259 return res.error();
260 }
261
262 if (new_hdr->header_version >= 4) {
263 auto hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(hdr);
264 const uint32_t r = round_up(hdr_v4->vendor_ramdisk_table_size, hdr_v4->page_size);
265 const uint32_t s = round_up(hdr_v4->bootconfig_size, hdr_v4->page_size);
266
267 auto new_entry = reinterpret_cast<vendor_ramdisk_table_entry_v4*>(updater.new_cur());
268 auto new_hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(new_hdr);
269 auto new_r = round_up(new_hdr_v4->vendor_ramdisk_table_size, new_hdr->page_size);
270 if (auto res = updater.Skip(r, new_r); !res.ok()) return res.error();
271 if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + new_q + new_r); !res.ok())
272 return res.error();
273
274 // Replace table with single entry representing the full ramdisk.
275 new_entry->ramdisk_size = new_hdr->vendor_ramdisk_size;
276 new_entry->ramdisk_offset = 0;
277 new_entry->ramdisk_type = VENDOR_RAMDISK_TYPE_NONE;
278 memset(new_entry->ramdisk_name, '\0', VENDOR_RAMDISK_NAME_SIZE);
279 memset(new_entry->board_id, '\0', VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE);
280
281 // Copy bootconfig (S bytes).
282 if (auto res = updater.Copy(s); !res.ok()) return res.error();
283 }
284
285 if (auto res = copy_avb_footer(&updater); !res.ok()) return res.error();
286 return updater.Finish();
287 }
288
289 // Find a ramdisk fragment with a unique name. Abort if none or multiple fragments are found.
find_unique_ramdisk(const std::string & ramdisk_name,const vendor_ramdisk_table_entry_v4 * table,uint32_t size)290 [[nodiscard]] Result<const vendor_ramdisk_table_entry_v4*> find_unique_ramdisk(
291 const std::string& ramdisk_name, const vendor_ramdisk_table_entry_v4* table,
292 uint32_t size) {
293 const vendor_ramdisk_table_entry_v4* ret = nullptr;
294 uint32_t idx = 0;
295 const vendor_ramdisk_table_entry_v4* entry = table;
296 for (; idx < size; idx++, entry++) {
297 auto entry_name_c_str = reinterpret_cast<const char*>(entry->ramdisk_name);
298 auto entry_name_len = strnlen(entry_name_c_str, VENDOR_RAMDISK_NAME_SIZE);
299 std::string_view entry_name(entry_name_c_str, entry_name_len);
300 if (entry_name == ramdisk_name) {
301 if (ret != nullptr) {
302 return Errorf("Multiple vendor ramdisk '{}' found, name should be unique",
303 ramdisk_name.c_str());
304 }
305 ret = entry;
306 }
307 }
308 if (ret == nullptr) {
309 return Errorf("Vendor ramdisk '{}' not found", ramdisk_name.c_str());
310 }
311 return ret;
312 }
313
314 // Find the vendor ramdisk fragment with |ramdisk_name| within the content of |vendor_boot|, and
315 // replace it with the content of |new_ramdisk|.
replace_vendor_ramdisk_fragment(const std::string & ramdisk_name,const std::string & vendor_boot,const std::string & new_ramdisk,const std::string & new_dtb)316 [[nodiscard]] Result<std::string> replace_vendor_ramdisk_fragment(const std::string& ramdisk_name,
317 const std::string& vendor_boot,
318 const std::string& new_ramdisk,
319 const std::string& new_dtb) {
320 if (auto res = check_vendor_boot_hdr(vendor_boot, 4); !res.ok()) return res.error();
321 auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v4*>(vendor_boot.data());
322 auto hdr_size = get_vendor_boot_header_size(hdr);
323 if (!hdr_size.ok()) return hdr_size.error();
324 // Refer to bootimg.h for details. Numbers are in bytes.
325 const uint32_t o = round_up(*hdr_size, hdr->page_size);
326 const uint32_t p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);
327 const uint32_t q = round_up(hdr->dtb_size, hdr->page_size);
328 const uint32_t r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);
329 const uint32_t s = round_up(hdr->bootconfig_size, hdr->page_size);
330
331 uint64_t total_size = (uint64_t)o + p + q + r + s;
332 if (total_size > vendor_boot.size()) {
333 return Errorf("Vendor boot image size is too small, overflow");
334 }
335
336 if ((uint64_t)hdr->vendor_ramdisk_table_entry_num * sizeof(vendor_ramdisk_table_entry_v4) >
337 (uint64_t)o + p + q + r) {
338 return Errorf("Too many vendor ramdisk entries in table, overflow");
339 }
340
341 // Find entry with name |ramdisk_name|.
342 auto old_table_start =
343 reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(vendor_boot.data() + o + p + q);
344 auto find_res =
345 find_unique_ramdisk(ramdisk_name, old_table_start, hdr->vendor_ramdisk_table_entry_num);
346 if (!find_res.ok()) return find_res.error();
347 const vendor_ramdisk_table_entry_v4* replace_entry = *find_res;
348 uint32_t replace_idx = replace_entry - old_table_start;
349
350 // Now reconstruct.
351 DataUpdater updater(vendor_boot);
352
353 // Copy header (O bytes), then update fields in header.
354 if (auto res = updater.Copy(o); !res.ok()) return res.error();
355 auto new_hdr = reinterpret_cast<vendor_boot_img_hdr_v4*>(updater.new_begin());
356
357 // Copy ramdisk fragments, replace for the matching index.
358 {
359 auto old_ramdisk_entry = reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(
360 vendor_boot.data() + o + p + q);
361 uint32_t new_total_ramdisk_size = 0;
362 for (uint32_t new_ramdisk_idx = 0; new_ramdisk_idx < hdr->vendor_ramdisk_table_entry_num;
363 new_ramdisk_idx++, old_ramdisk_entry++) {
364 if (new_ramdisk_idx == replace_idx) {
365 if (auto res = updater.Replace(replace_entry->ramdisk_size, new_ramdisk); !res.ok())
366 return res.error();
367 new_total_ramdisk_size += new_ramdisk.size();
368 } else {
369 if (auto res = updater.Copy(old_ramdisk_entry->ramdisk_size); !res.ok())
370 return res.error();
371 new_total_ramdisk_size += old_ramdisk_entry->ramdisk_size;
372 }
373 }
374 new_hdr->vendor_ramdisk_size = new_total_ramdisk_size;
375 }
376
377 // Pad ramdisk to page boundary.
378 const uint32_t new_p = round_up(new_hdr->vendor_ramdisk_size, new_hdr->page_size);
379 if (auto res = updater.Skip(p - hdr->vendor_ramdisk_size, new_p - new_hdr->vendor_ramdisk_size);
380 !res.ok())
381 return res.error();
382 if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error();
383
384 // Copy DTB (Q bytes). Replace if a new one was provided.
385 new_hdr->dtb_size = !new_dtb.empty() ? new_dtb.size() : hdr->dtb_size;
386 const uint32_t new_q = round_up(new_hdr->dtb_size, new_hdr->page_size);
387 if (new_dtb.empty()) {
388 if (auto res = updater.Copy(q); !res.ok()) return res.error();
389 } else {
390 if (auto res = updater.Replace(hdr->dtb_size, new_dtb); !res.ok()) return res.error();
391 if (auto res = updater.Skip(q - hdr->dtb_size, new_q - new_hdr->dtb_size); !res.ok())
392 return res.error();
393 }
394 if (auto res = updater.CheckOffset(o + p + q, o + new_p + new_q); !res.ok()) {
395 return res.error();
396 }
397
398 // Copy table, but with corresponding entries modified, including:
399 // - ramdisk_size of the entry replaced
400 // - ramdisk_offset of subsequent entries.
401 for (uint32_t new_total_ramdisk_size = 0, new_entry_idx = 0;
402 new_entry_idx < hdr->vendor_ramdisk_table_entry_num; new_entry_idx++) {
403 auto new_entry = reinterpret_cast<vendor_ramdisk_table_entry_v4*>(updater.new_cur());
404 if (auto res = updater.Copy(hdr->vendor_ramdisk_table_entry_size); !res.ok())
405 return res.error();
406 new_entry->ramdisk_offset = new_total_ramdisk_size;
407
408 if (new_entry_idx == replace_idx) {
409 new_entry->ramdisk_size = new_ramdisk.size();
410 }
411 new_total_ramdisk_size += new_entry->ramdisk_size;
412 }
413
414 // Copy padding of R pages; this is okay because table size is not changed.
415 if (auto res = updater.Copy(r - hdr->vendor_ramdisk_table_entry_num *
416 hdr->vendor_ramdisk_table_entry_size);
417 !res.ok())
418 return res.error();
419 if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + new_q + r); !res.ok())
420 return res.error();
421
422 // Copy bootconfig (S bytes).
423 if (auto res = updater.Copy(s); !res.ok()) return res.error();
424
425 if (auto res = copy_avb_footer(&updater); !res.ok()) return res.error();
426 return updater.Finish();
427 }
428
429 } // namespace
430
replace_vendor_ramdisk(android::base::borrowed_fd vendor_boot_fd,uint64_t vendor_boot_size,const std::string & ramdisk_name,android::base::borrowed_fd new_ramdisk_fd,uint64_t new_ramdisk_size,android::base::borrowed_fd new_dtb_fd,uint64_t new_dtb_size)431 [[nodiscard]] Result<void> replace_vendor_ramdisk(
432 android::base::borrowed_fd vendor_boot_fd, uint64_t vendor_boot_size,
433 const std::string& ramdisk_name, android::base::borrowed_fd new_ramdisk_fd,
434 uint64_t new_ramdisk_size, android::base::borrowed_fd new_dtb_fd, uint64_t new_dtb_size) {
435 Result<std::string> new_dtb = {""};
436 if (new_ramdisk_size > std::numeric_limits<uint32_t>::max()) {
437 return Errorf("New vendor ramdisk is too big");
438 }
439
440 auto vendor_boot = load_file(vendor_boot_fd, vendor_boot_size, "vendor boot");
441 if (!vendor_boot.ok()) return vendor_boot.error();
442 auto new_ramdisk = load_file(new_ramdisk_fd, new_ramdisk_size, "new vendor ramdisk");
443 if (!new_ramdisk.ok()) return new_ramdisk.error();
444 if (new_dtb_size > 0 && new_dtb_fd >= 0) {
445 new_dtb = load_file(new_dtb_fd, new_dtb_size, "new dtb");
446 if (!new_dtb.ok()) return new_dtb.error();
447 }
448
449 Result<std::string> new_vendor_boot;
450 if (ramdisk_name == "default") {
451 new_vendor_boot = replace_default_vendor_ramdisk(*vendor_boot, *new_ramdisk, *new_dtb);
452 } else {
453 new_vendor_boot =
454 replace_vendor_ramdisk_fragment(ramdisk_name, *vendor_boot, *new_ramdisk, *new_dtb);
455 }
456 if (!new_vendor_boot.ok()) return new_vendor_boot.error();
457 if (auto res = store_file(vendor_boot_fd, *new_vendor_boot, "new vendor boot image"); !res.ok())
458 return res.error();
459
460 return {};
461 }
462