blob: c58a80203eb0e5879a110da4fba708c4b1dd0f3c [file] [log] [blame]
Kelvin Zhang50bac652020-09-28 15:51:41 -04001//
2// Copyright (C) 2020 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#include <update_engine/payload_consumer/partition_writer.h>
17
18#include <fcntl.h>
19#include <linux/fs.h>
Kelvin Zhang1d402cb2021-02-09 15:28:16 -050020#include <sys/mman.h>
Kelvin Zhang50bac652020-09-28 15:51:41 -040021
22#include <algorithm>
23#include <initializer_list>
24#include <memory>
25#include <utility>
26#include <vector>
27
Kelvin Zhang1d402cb2021-02-09 15:28:16 -050028#include <base/files/memory_mapped_file.h>
Kelvin Zhang50bac652020-09-28 15:51:41 -040029#include <base/strings/string_number_conversions.h>
30#include <bsdiff/bspatch.h>
31#include <puffin/puffpatch.h>
32#include <bsdiff/file_interface.h>
33#include <puffin/stream.h>
34
35#include "update_engine/common/terminator.h"
36#include "update_engine/common/utils.h"
37#include "update_engine/payload_consumer/bzip_extent_writer.h"
38#include "update_engine/payload_consumer/cached_file_descriptor.h"
39#include "update_engine/payload_consumer/extent_reader.h"
40#include "update_engine/payload_consumer/extent_writer.h"
41#include "update_engine/payload_consumer/fec_file_descriptor.h"
42#include "update_engine/payload_consumer/file_descriptor_utils.h"
43#include "update_engine/payload_consumer/install_plan.h"
44#include "update_engine/payload_consumer/mount_history.h"
45#include "update_engine/payload_consumer/payload_constants.h"
46#include "update_engine/payload_consumer/xz_extent_writer.h"
Kelvin Zhang37b9b702021-02-23 10:30:37 -050047#include "update_engine/payload_generator/extent_utils.h"
Kelvin Zhang50bac652020-09-28 15:51:41 -040048
49namespace chromeos_update_engine {
50
51namespace {
52constexpr uint64_t kCacheSize = 1024 * 1024; // 1MB
53
54// Discard the tail of the block device referenced by |fd|, from the offset
55// |data_size| until the end of the block device. Returns whether the data was
56// discarded.
57
58bool DiscardPartitionTail(const FileDescriptorPtr& fd, uint64_t data_size) {
59 uint64_t part_size = fd->BlockDevSize();
60 if (!part_size || part_size <= data_size)
61 return false;
62
63 struct blkioctl_request {
64 int number;
65 const char* name;
66 };
67 const std::initializer_list<blkioctl_request> blkioctl_requests = {
68 {BLKDISCARD, "BLKDISCARD"},
69 {BLKSECDISCARD, "BLKSECDISCARD"},
70#ifdef BLKZEROOUT
71 {BLKZEROOUT, "BLKZEROOUT"},
72#endif
73 };
74 for (const auto& req : blkioctl_requests) {
75 int error = 0;
76 if (fd->BlkIoctl(req.number, data_size, part_size - data_size, &error) &&
77 error == 0) {
78 return true;
79 }
80 LOG(WARNING) << "Error discarding the last "
81 << (part_size - data_size) / 1024 << " KiB using ioctl("
82 << req.name << ")";
83 }
84 return false;
85}
86
87} // namespace
88
Kelvin Zhang37b9b702021-02-23 10:30:37 -050089using google::protobuf::RepeatedPtrField;
90
Kelvin Zhang50bac652020-09-28 15:51:41 -040091// Opens path for read/write. On success returns an open FileDescriptor
92// and sets *err to 0. On failure, sets *err to errno and returns nullptr.
93FileDescriptorPtr OpenFile(const char* path,
94 int mode,
95 bool cache_writes,
96 int* err) {
97 // Try to mark the block device read-only based on the mode. Ignore any
98 // failure since this won't work when passing regular files.
99 bool read_only = (mode & O_ACCMODE) == O_RDONLY;
100 utils::SetBlockDeviceReadOnly(path, read_only);
101
102 FileDescriptorPtr fd(new EintrSafeFileDescriptor());
103 if (cache_writes && !read_only) {
104 fd = FileDescriptorPtr(new CachedFileDescriptor(fd, kCacheSize));
105 LOG(INFO) << "Caching writes.";
106 }
107 if (!fd->Open(path, mode, 000)) {
108 *err = errno;
109 PLOG(ERROR) << "Unable to open file " << path;
110 return nullptr;
111 }
112 *err = 0;
113 return fd;
114}
115
116class BsdiffExtentFile : public bsdiff::FileInterface {
117 public:
118 BsdiffExtentFile(std::unique_ptr<ExtentReader> reader, size_t size)
119 : BsdiffExtentFile(std::move(reader), nullptr, size) {}
120 BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer, size_t size)
121 : BsdiffExtentFile(nullptr, std::move(writer), size) {}
122
123 ~BsdiffExtentFile() override = default;
124
125 bool Read(void* buf, size_t count, size_t* bytes_read) override {
126 TEST_AND_RETURN_FALSE(reader_->Read(buf, count));
127 *bytes_read = count;
128 offset_ += count;
129 return true;
130 }
131
132 bool Write(const void* buf, size_t count, size_t* bytes_written) override {
133 TEST_AND_RETURN_FALSE(writer_->Write(buf, count));
134 *bytes_written = count;
135 offset_ += count;
136 return true;
137 }
138
139 bool Seek(off_t pos) override {
140 if (reader_ != nullptr) {
141 TEST_AND_RETURN_FALSE(reader_->Seek(pos));
142 offset_ = pos;
143 } else {
144 // For writes technically there should be no change of position, or it
145 // should be equivalent of current offset.
146 TEST_AND_RETURN_FALSE(offset_ == static_cast<uint64_t>(pos));
147 }
148 return true;
149 }
150
151 bool Close() override { return true; }
152
153 bool GetSize(uint64_t* size) override {
154 *size = size_;
155 return true;
156 }
157
158 private:
159 BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,
160 std::unique_ptr<ExtentWriter> writer,
161 size_t size)
162 : reader_(std::move(reader)),
163 writer_(std::move(writer)),
164 size_(size),
165 offset_(0) {}
166
167 std::unique_ptr<ExtentReader> reader_;
168 std::unique_ptr<ExtentWriter> writer_;
169 uint64_t size_;
170 uint64_t offset_;
171
172 DISALLOW_COPY_AND_ASSIGN(BsdiffExtentFile);
173};
174// A class to be passed to |puffpatch| for reading from |source_fd_| and writing
175// into |target_fd_|.
176class PuffinExtentStream : public puffin::StreamInterface {
177 public:
178 // Constructor for creating a stream for reading from an |ExtentReader|.
179 PuffinExtentStream(std::unique_ptr<ExtentReader> reader, uint64_t size)
180 : PuffinExtentStream(std::move(reader), nullptr, size) {}
181
182 // Constructor for creating a stream for writing to an |ExtentWriter|.
183 PuffinExtentStream(std::unique_ptr<ExtentWriter> writer, uint64_t size)
184 : PuffinExtentStream(nullptr, std::move(writer), size) {}
185
186 ~PuffinExtentStream() override = default;
187
188 bool GetSize(uint64_t* size) const override {
189 *size = size_;
190 return true;
191 }
192
193 bool GetOffset(uint64_t* offset) const override {
194 *offset = offset_;
195 return true;
196 }
197
198 bool Seek(uint64_t offset) override {
199 if (is_read_) {
200 TEST_AND_RETURN_FALSE(reader_->Seek(offset));
201 offset_ = offset;
202 } else {
203 // For writes technically there should be no change of position, or it
204 // should equivalent of current offset.
205 TEST_AND_RETURN_FALSE(offset_ == offset);
206 }
207 return true;
208 }
209
210 bool Read(void* buffer, size_t count) override {
211 TEST_AND_RETURN_FALSE(is_read_);
212 TEST_AND_RETURN_FALSE(reader_->Read(buffer, count));
213 offset_ += count;
214 return true;
215 }
216
217 bool Write(const void* buffer, size_t count) override {
218 TEST_AND_RETURN_FALSE(!is_read_);
219 TEST_AND_RETURN_FALSE(writer_->Write(buffer, count));
220 offset_ += count;
221 return true;
222 }
223
224 bool Close() override { return true; }
225
226 private:
227 PuffinExtentStream(std::unique_ptr<ExtentReader> reader,
228 std::unique_ptr<ExtentWriter> writer,
229 uint64_t size)
230 : reader_(std::move(reader)),
231 writer_(std::move(writer)),
232 size_(size),
233 offset_(0),
234 is_read_(reader_ ? true : false) {}
235
236 std::unique_ptr<ExtentReader> reader_;
237 std::unique_ptr<ExtentWriter> writer_;
238 uint64_t size_;
239 uint64_t offset_;
240 bool is_read_;
241
242 DISALLOW_COPY_AND_ASSIGN(PuffinExtentStream);
243};
244
245PartitionWriter::PartitionWriter(
246 const PartitionUpdate& partition_update,
247 const InstallPlan::Partition& install_part,
248 DynamicPartitionControlInterface* dynamic_control,
249 size_t block_size,
250 bool is_interactive)
251 : partition_update_(partition_update),
252 install_part_(install_part),
253 dynamic_control_(dynamic_control),
254 interactive_(is_interactive),
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500255 block_size_(block_size),
256 install_op_executor_(block_size) {}
Kelvin Zhang50bac652020-09-28 15:51:41 -0400257
258PartitionWriter::~PartitionWriter() {
259 Close();
260}
261
Kelvin Zhang3f60d532020-11-09 13:33:17 -0500262bool PartitionWriter::OpenSourcePartition(uint32_t source_slot,
263 bool source_may_exist) {
264 source_path_.clear();
265 if (!source_may_exist) {
266 return true;
267 }
268 if (install_part_.source_size > 0 && !install_part_.source_path.empty()) {
269 source_path_ = install_part_.source_path;
270 int err;
271 source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
272 if (source_fd_ == nullptr) {
273 LOG(ERROR) << "Unable to open source partition " << install_part_.name
274 << " on slot " << BootControlInterface::SlotName(source_slot)
275 << ", file " << source_path_;
276 return false;
277 }
278 }
279 return true;
280}
281
Kelvin Zhang50bac652020-09-28 15:51:41 -0400282bool PartitionWriter::Init(const InstallPlan* install_plan,
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400283 bool source_may_exist,
284 size_t next_op_index) {
Kelvin Zhang50bac652020-09-28 15:51:41 -0400285 const PartitionUpdate& partition = partition_update_;
286 uint32_t source_slot = install_plan->source_slot;
287 uint32_t target_slot = install_plan->target_slot;
Kelvin Zhang3f60d532020-11-09 13:33:17 -0500288 TEST_AND_RETURN_FALSE(OpenSourcePartition(source_slot, source_may_exist));
Kelvin Zhang50bac652020-09-28 15:51:41 -0400289
290 // We shouldn't open the source partition in certain cases, e.g. some dynamic
291 // partitions in delta payload, partitions included in the full payload for
292 // partial updates. Use the source size as the indicator.
Kelvin Zhang50bac652020-09-28 15:51:41 -0400293
294 target_path_ = install_part_.target_path;
295 int err;
296
297 int flags = O_RDWR;
298 if (!interactive_)
299 flags |= O_DSYNC;
300
301 LOG(INFO) << "Opening " << target_path_ << " partition with"
302 << (interactive_ ? "out" : "") << " O_DSYNC";
303
304 target_fd_ = OpenFile(target_path_.c_str(), flags, true, &err);
305 if (!target_fd_) {
306 LOG(ERROR) << "Unable to open target partition "
307 << partition.partition_name() << " on slot "
308 << BootControlInterface::SlotName(target_slot) << ", file "
309 << target_path_;
310 return false;
311 }
312
313 LOG(INFO) << "Applying " << partition.operations().size()
314 << " operations to partition \"" << partition.partition_name()
315 << "\"";
316
317 // Discard the end of the partition, but ignore failures.
318 DiscardPartitionTail(target_fd_, install_part_.target_size);
319
320 return true;
321}
322
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500323bool InstallOperationExecutor::ExecuteReplaceOperation(
324 const InstallOperation& operation,
325 std::unique_ptr<ExtentWriter> writer,
326 const void* data,
327 size_t count) {
328 TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::REPLACE ||
329 operation.type() == InstallOperation::REPLACE_BZ ||
330 operation.type() == InstallOperation::REPLACE_XZ);
Kelvin Zhang50bac652020-09-28 15:51:41 -0400331 // Setup the ExtentWriter stack based on the operation type.
Kelvin Zhang50bac652020-09-28 15:51:41 -0400332 if (operation.type() == InstallOperation::REPLACE_BZ) {
333 writer.reset(new BzipExtentWriter(std::move(writer)));
334 } else if (operation.type() == InstallOperation::REPLACE_XZ) {
335 writer.reset(new XzExtentWriter(std::move(writer)));
336 }
Kelvin Zhang4d22ca22021-02-09 14:06:25 -0500337 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
Kelvin Zhang50bac652020-09-28 15:51:41 -0400338 TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
339
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400340 return true;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400341}
342
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500343bool PartitionWriter::PerformReplaceOperation(const InstallOperation& operation,
344 const void* data,
345 size_t count) {
346 // Setup the ExtentWriter stack based on the operation type.
347 std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
348 writer->Init(operation.dst_extents(), block_size_);
349 return install_op_executor_.ExecuteReplaceOperation(
350 operation, std::move(writer), data, count);
351}
352
353bool InstallOperationExecutor::ExecuteZeroOrDiscardOperation(
354 const InstallOperation& operation, ExtentWriter* writer) {
355 TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::ZERO ||
356 operation.type() == InstallOperation::DISCARD);
357 for (const auto& extent : operation.dst_extents()) {
358 // Mmap a region of /dev/zero, as we don't need any actual memory to store
359 // these 0s, so mmap a region of "free memory".
360 base::File dev_zero(base::FilePath("/dev/zero"),
361 base::File::FLAG_OPEN | base::File::FLAG_READ);
362 base::MemoryMappedFile buffer;
363 TEST_AND_RETURN_FALSE_ERRNO(buffer.Initialize(
364 std::move(dev_zero),
365 base::MemoryMappedFile::Region{
366 0, static_cast<size_t>(extent.num_blocks() * block_size_)},
367 base::MemoryMappedFile::Access::READ_ONLY));
368 TEST_AND_RETURN_FALSE(buffer.data() != nullptr);
369 writer->Write(buffer.data(), buffer.length());
370 }
371 return true;
372}
373
Kelvin Zhang50bac652020-09-28 15:51:41 -0400374bool PartitionWriter::PerformZeroOrDiscardOperation(
375 const InstallOperation& operation) {
376#ifdef BLKZEROOUT
Kelvin Zhang50bac652020-09-28 15:51:41 -0400377 int request =
378 (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
379#else // !defined(BLKZEROOUT)
Kelvin Zhangf4575862021-02-24 10:46:32 -0500380 auto writer = CreateBaseExtentWriter();
381 return install_op_executor_.ExecuteZeroOrDiscardOperation(operation,
382 writer.get());
Kelvin Zhang50bac652020-09-28 15:51:41 -0400383#endif // !defined(BLKZEROOUT)
384
Kelvin Zhang50bac652020-09-28 15:51:41 -0400385 for (const Extent& extent : operation.dst_extents()) {
386 const uint64_t start = extent.start_block() * block_size_;
387 const uint64_t length = extent.num_blocks() * block_size_;
Kelvin Zhangf4575862021-02-24 10:46:32 -0500388 int result = 0;
389 if (target_fd_->BlkIoctl(request, start, length, &result) && result == 0) {
390 continue;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400391 }
Kelvin Zhangf4575862021-02-24 10:46:32 -0500392 // In case of failure, we fall back to writing 0 for the entire operation.
393 PLOG(WARNING) << "BlkIoctl failed. Falling back to write 0s for remainder "
394 "of this operation.";
395 auto writer = CreateBaseExtentWriter();
396 writer->Init(operation.dst_extents(), block_size_);
397 return install_op_executor_.ExecuteZeroOrDiscardOperation(operation,
398 writer.get());
Kelvin Zhang50bac652020-09-28 15:51:41 -0400399 }
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400400 return true;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400401}
402
Kelvin Zhang37b9b702021-02-23 10:30:37 -0500403std::ostream& operator<<(std::ostream& out,
404 const RepeatedPtrField<Extent>& extents) {
405 if (extents.size() == 0) {
406 out << "[]";
407 return out;
408 }
409 out << "[";
410 auto begin = extents.begin();
411 out << *begin;
412 for (int i = 1; i < extents.size(); i++) {
413 ++begin;
414 out << ", " << *begin;
415 }
416 out << "]";
417 return out;
418}
419
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500420bool InstallOperationExecutor::ExecuteSourceCopyOperation(
421 const InstallOperation& operation,
422 ExtentWriter* writer,
423 FileDescriptorPtr source_fd) {
424 TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::SOURCE_COPY);
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500425 return fd_utils::CommonHashExtents(
426 source_fd, operation.src_extents(), writer, block_size_, nullptr);
427}
428
Kelvin Zhang50bac652020-09-28 15:51:41 -0400429bool PartitionWriter::PerformSourceCopyOperation(
430 const InstallOperation& operation, ErrorCode* error) {
431 TEST_AND_RETURN_FALSE(source_fd_ != nullptr);
432
433 // The device may optimize the SOURCE_COPY operation.
434 // Being this a device-specific optimization let DynamicPartitionController
435 // decide it the operation should be skipped.
436 const PartitionUpdate& partition = partition_update_;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400437
438 InstallOperation buf;
Kelvin Zhang37b9b702021-02-23 10:30:37 -0500439 const bool should_optimize = dynamic_control_->OptimizeOperation(
Kelvin Zhang50bac652020-09-28 15:51:41 -0400440 partition.partition_name(), operation, &buf);
441 const InstallOperation& optimized = should_optimize ? buf : operation;
442
Kelvin Zhang37b9b702021-02-23 10:30:37 -0500443 // Invoke ChooseSourceFD with original operation, so that it can properly
444 // verify source hashes. Optimized operation might contain a smaller set of
445 // extents, or completely empty.
446 auto source_fd = ChooseSourceFD(operation, error);
447 if (source_fd == nullptr) {
448 LOG(ERROR) << "Unrecoverable source hash mismatch found on partition "
449 << partition.partition_name()
450 << " extents: " << operation.src_extents();
451 return false;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400452 }
Kelvin Zhang37b9b702021-02-23 10:30:37 -0500453
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500454 auto writer = CreateBaseExtentWriter();
455 writer->Init(optimized.dst_extents(), block_size_);
456 return install_op_executor_.ExecuteSourceCopyOperation(
457 optimized, writer.get(), source_fd);
Kelvin Zhang50bac652020-09-28 15:51:41 -0400458}
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400459
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500460bool InstallOperationExecutor::ExecuteSourceBsdiffOperation(
Kelvin Zhang50bac652020-09-28 15:51:41 -0400461 const InstallOperation& operation,
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500462 std::unique_ptr<ExtentWriter> writer,
463 FileDescriptorPtr source_fd,
Kelvin Zhang50bac652020-09-28 15:51:41 -0400464 const void* data,
465 size_t count) {
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500466 TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::SOURCE_BSDIFF ||
467 operation.type() == InstallOperation::BROTLI_BSDIFF ||
468 operation.type() == InstallOperation::BSDIFF);
Kelvin Zhang50bac652020-09-28 15:51:41 -0400469 TEST_AND_RETURN_FALSE(source_fd != nullptr);
470
471 auto reader = std::make_unique<DirectExtentReader>();
472 TEST_AND_RETURN_FALSE(
473 reader->Init(source_fd, operation.src_extents(), block_size_));
474 auto src_file = std::make_unique<BsdiffExtentFile>(
475 std::move(reader),
476 utils::BlocksInExtents(operation.src_extents()) * block_size_);
477
Kelvin Zhang4d22ca22021-02-09 14:06:25 -0500478 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
Kelvin Zhang50bac652020-09-28 15:51:41 -0400479 auto dst_file = std::make_unique<BsdiffExtentFile>(
480 std::move(writer),
481 utils::BlocksInExtents(operation.dst_extents()) * block_size_);
482
483 TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
484 std::move(dst_file),
485 reinterpret_cast<const uint8_t*>(data),
486 count) == 0);
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400487 return true;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400488}
489
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500490bool PartitionWriter::PerformSourceBsdiffOperation(
Kelvin Zhang50bac652020-09-28 15:51:41 -0400491 const InstallOperation& operation,
492 ErrorCode* error,
493 const void* data,
494 size_t count) {
495 FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
496 TEST_AND_RETURN_FALSE(source_fd != nullptr);
497
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500498 auto writer = CreateBaseExtentWriter();
499 writer->Init(operation.dst_extents(), block_size_);
500 return install_op_executor_.ExecuteSourceBsdiffOperation(
501 operation, std::move(writer), source_fd, data, count);
502}
503
504bool InstallOperationExecutor::ExecutePuffDiffOperation(
505 const InstallOperation& operation,
506 std::unique_ptr<ExtentWriter> writer,
507 FileDescriptorPtr source_fd,
508 const void* data,
509 size_t count) {
510 TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::PUFFDIFF);
511 TEST_AND_RETURN_FALSE(source_fd != nullptr);
512
Kelvin Zhang50bac652020-09-28 15:51:41 -0400513 auto reader = std::make_unique<DirectExtentReader>();
514 TEST_AND_RETURN_FALSE(
515 reader->Init(source_fd, operation.src_extents(), block_size_));
516 puffin::UniqueStreamPtr src_stream(new PuffinExtentStream(
517 std::move(reader),
518 utils::BlocksInExtents(operation.src_extents()) * block_size_));
519
Kelvin Zhang4d22ca22021-02-09 14:06:25 -0500520 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
Kelvin Zhang50bac652020-09-28 15:51:41 -0400521 puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
522 std::move(writer),
523 utils::BlocksInExtents(operation.dst_extents()) * block_size_));
524
525 constexpr size_t kMaxCacheSize = 5 * 1024 * 1024; // Total 5MB cache.
526 TEST_AND_RETURN_FALSE(
527 puffin::PuffPatch(std::move(src_stream),
528 std::move(dst_stream),
529 reinterpret_cast<const uint8_t*>(data),
530 count,
531 kMaxCacheSize));
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400532 return true;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400533}
534
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500535bool PartitionWriter::PerformPuffDiffOperation(
536 const InstallOperation& operation,
537 ErrorCode* error,
538 const void* data,
539 size_t count) {
540 FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
541 TEST_AND_RETURN_FALSE(source_fd != nullptr);
542
543 auto writer = CreateBaseExtentWriter();
544 writer->Init(operation.dst_extents(), block_size_);
545 return install_op_executor_.ExecutePuffDiffOperation(
546 operation, std::move(writer), source_fd, data, count);
547}
548
Kelvin Zhang50bac652020-09-28 15:51:41 -0400549FileDescriptorPtr PartitionWriter::ChooseSourceFD(
550 const InstallOperation& operation, ErrorCode* error) {
551 if (source_fd_ == nullptr) {
552 LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
553 return nullptr;
554 }
555
556 if (!operation.has_src_sha256_hash()) {
557 // When the operation doesn't include a source hash, we attempt the error
Kelvin Zhangf4575862021-02-24 10:46:32 -0500558 // corrected device first since we can't verify the block in the raw
559 // device at this point, but we first need to make sure all extents are
560 // readable since the error corrected device can be shorter or not
561 // available.
Kelvin Zhang50bac652020-09-28 15:51:41 -0400562 if (OpenCurrentECCPartition() &&
563 fd_utils::ReadAndHashExtents(
564 source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
565 return source_ecc_fd_;
566 }
567 return source_fd_;
568 }
569
570 brillo::Blob source_hash;
571 brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
572 operation.src_sha256_hash().end());
573 if (fd_utils::ReadAndHashExtents(
574 source_fd_, operation.src_extents(), block_size_, &source_hash) &&
575 source_hash == expected_source_hash) {
576 return source_fd_;
577 }
578 // We fall back to use the error corrected device if the hash of the raw
579 // device doesn't match or there was an error reading the source partition.
580 if (!OpenCurrentECCPartition()) {
581 // The following function call will return false since the source hash
582 // mismatches, but we still want to call it so it prints the appropriate
583 // log message.
584 ValidateSourceHash(source_hash, operation, source_fd_, error);
585 return nullptr;
586 }
587 LOG(WARNING) << "Source hash from RAW device mismatched: found "
588 << base::HexEncode(source_hash.data(), source_hash.size())
589 << ", expected "
590 << base::HexEncode(expected_source_hash.data(),
591 expected_source_hash.size());
592
593 if (fd_utils::ReadAndHashExtents(
594 source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
595 ValidateSourceHash(source_hash, operation, source_ecc_fd_, error)) {
596 // At this point reading from the error corrected device worked, but
597 // reading from the raw device failed, so this is considered a recovered
598 // failure.
599 source_ecc_recovered_failures_++;
600 return source_ecc_fd_;
601 }
602 return nullptr;
603}
604
605bool PartitionWriter::OpenCurrentECCPartition() {
606 // No support for ECC for full payloads.
607 // Full payload should not have any opeartion that requires ECC partitions.
608 if (source_ecc_fd_)
609 return true;
610
611 if (source_ecc_open_failure_)
612 return false;
613
614#if USE_FEC
615 const PartitionUpdate& partition = partition_update_;
616 const InstallPlan::Partition& install_part = install_part_;
617 std::string path = install_part.source_path;
618 FileDescriptorPtr fd(new FecFileDescriptor());
619 if (!fd->Open(path.c_str(), O_RDONLY, 0)) {
620 PLOG(ERROR) << "Unable to open ECC source partition "
621 << partition.partition_name() << ", file " << path;
622 source_ecc_open_failure_ = true;
623 return false;
624 }
625 source_ecc_fd_ = fd;
626#else
627 // No support for ECC compiled.
628 source_ecc_open_failure_ = true;
629#endif // USE_FEC
630
631 return !source_ecc_open_failure_;
632}
633
634int PartitionWriter::Close() {
635 int err = 0;
636 if (source_fd_ && !source_fd_->Close()) {
637 err = errno;
638 PLOG(ERROR) << "Error closing source partition";
639 if (!err)
640 err = 1;
641 }
642 source_fd_.reset();
643 source_path_.clear();
644
645 if (target_fd_ && !target_fd_->Close()) {
646 err = errno;
647 PLOG(ERROR) << "Error closing target partition";
648 if (!err)
649 err = 1;
650 }
651 target_fd_.reset();
652 target_path_.clear();
653
654 if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
655 err = errno;
656 PLOG(ERROR) << "Error closing ECC source partition";
657 if (!err)
658 err = 1;
659 }
660 source_ecc_fd_.reset();
661 source_ecc_open_failure_ = false;
662 return -err;
663}
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400664
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400665void PartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
666 target_fd_->Flush();
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400667}
668
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400669std::unique_ptr<ExtentWriter> PartitionWriter::CreateBaseExtentWriter() {
Kelvin Zhang4d22ca22021-02-09 14:06:25 -0500670 return std::make_unique<DirectExtentWriter>(target_fd_);
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400671}
672
Kelvin Zhang1d402cb2021-02-09 15:28:16 -0500673bool InstallOperationExecutor::ExecuteInstallOp(
674 const InstallOperation& op,
675 std::unique_ptr<ExtentWriter> writer,
676 FileDescriptorPtr source_fd,
677 const void* data,
678 size_t size) {
679 switch (op.type()) {
680 case InstallOperation::REPLACE:
681 case InstallOperation::REPLACE_BZ:
682 case InstallOperation::REPLACE_XZ:
683 return ExecuteReplaceOperation(op, std::move(writer), data, size);
684 case InstallOperation::ZERO:
685 case InstallOperation::DISCARD:
686 return ExecuteZeroOrDiscardOperation(op, writer.get());
687 case InstallOperation::SOURCE_COPY:
688 return ExecuteSourceCopyOperation(op, writer.get(), source_fd);
689 case InstallOperation::SOURCE_BSDIFF:
690 case InstallOperation::BROTLI_BSDIFF:
691 return ExecuteSourceBsdiffOperation(
692 op, std::move(writer), source_fd, data, size);
693 case InstallOperation::PUFFDIFF:
694 return ExecutePuffDiffOperation(
695 op, std::move(writer), source_fd, data, size);
696 break;
697 default:
698 return false;
699 }
700 return false;
701}
702
Kelvin Zhang50bac652020-09-28 15:51:41 -0400703} // namespace chromeos_update_engine