1 //
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>
20
21 #include <algorithm>
22 #include <initializer_list>
23 #include <memory>
24 #include <utility>
25 #include <vector>
26
27 #include <base/strings/string_number_conversions.h>
28 #include <bsdiff/bspatch.h>
29 #include <puffin/puffpatch.h>
30 #include <bsdiff/file_interface.h>
31 #include <puffin/stream.h>
32
33 #include "update_engine/common/terminator.h"
34 #include "update_engine/common/utils.h"
35 #include "update_engine/payload_consumer/bzip_extent_writer.h"
36 #include "update_engine/payload_consumer/cached_file_descriptor.h"
37 #include "update_engine/payload_consumer/extent_reader.h"
38 #include "update_engine/payload_consumer/extent_writer.h"
39 #include "update_engine/payload_consumer/fec_file_descriptor.h"
40 #include "update_engine/payload_consumer/file_descriptor_utils.h"
41 #include "update_engine/payload_consumer/install_plan.h"
42 #include "update_engine/payload_consumer/mount_history.h"
43 #include "update_engine/payload_consumer/payload_constants.h"
44 #include "update_engine/payload_consumer/xz_extent_writer.h"
45
46 namespace chromeos_update_engine {
47
48 namespace {
49 constexpr uint64_t kCacheSize = 1024 * 1024; // 1MB
50
51 // Discard the tail of the block device referenced by |fd|, from the offset
52 // |data_size| until the end of the block device. Returns whether the data was
53 // discarded.
54
DiscardPartitionTail(const FileDescriptorPtr & fd,uint64_t data_size)55 bool DiscardPartitionTail(const FileDescriptorPtr& fd, uint64_t data_size) {
56 uint64_t part_size = fd->BlockDevSize();
57 if (!part_size || part_size <= data_size)
58 return false;
59
60 struct blkioctl_request {
61 int number;
62 const char* name;
63 };
64 const std::initializer_list<blkioctl_request> blkioctl_requests = {
65 {BLKDISCARD, "BLKDISCARD"},
66 {BLKSECDISCARD, "BLKSECDISCARD"},
67 #ifdef BLKZEROOUT
68 {BLKZEROOUT, "BLKZEROOUT"},
69 #endif
70 };
71 for (const auto& req : blkioctl_requests) {
72 int error = 0;
73 if (fd->BlkIoctl(req.number, data_size, part_size - data_size, &error) &&
74 error == 0) {
75 return true;
76 }
77 LOG(WARNING) << "Error discarding the last "
78 << (part_size - data_size) / 1024 << " KiB using ioctl("
79 << req.name << ")";
80 }
81 return false;
82 }
83
84 } // namespace
85
86 // Opens path for read/write. On success returns an open FileDescriptor
87 // and sets *err to 0. On failure, sets *err to errno and returns nullptr.
OpenFile(const char * path,int mode,bool cache_writes,int * err)88 FileDescriptorPtr OpenFile(const char* path,
89 int mode,
90 bool cache_writes,
91 int* err) {
92 // Try to mark the block device read-only based on the mode. Ignore any
93 // failure since this won't work when passing regular files.
94 bool read_only = (mode & O_ACCMODE) == O_RDONLY;
95 utils::SetBlockDeviceReadOnly(path, read_only);
96
97 FileDescriptorPtr fd(new EintrSafeFileDescriptor());
98 if (cache_writes && !read_only) {
99 fd = FileDescriptorPtr(new CachedFileDescriptor(fd, kCacheSize));
100 LOG(INFO) << "Caching writes.";
101 }
102 if (!fd->Open(path, mode, 000)) {
103 *err = errno;
104 PLOG(ERROR) << "Unable to open file " << path;
105 return nullptr;
106 }
107 *err = 0;
108 return fd;
109 }
110
111 class BsdiffExtentFile : public bsdiff::FileInterface {
112 public:
BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,size_t size)113 BsdiffExtentFile(std::unique_ptr<ExtentReader> reader, size_t size)
114 : BsdiffExtentFile(std::move(reader), nullptr, size) {}
BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer,size_t size)115 BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer, size_t size)
116 : BsdiffExtentFile(nullptr, std::move(writer), size) {}
117
118 ~BsdiffExtentFile() override = default;
119
Read(void * buf,size_t count,size_t * bytes_read)120 bool Read(void* buf, size_t count, size_t* bytes_read) override {
121 TEST_AND_RETURN_FALSE(reader_->Read(buf, count));
122 *bytes_read = count;
123 offset_ += count;
124 return true;
125 }
126
Write(const void * buf,size_t count,size_t * bytes_written)127 bool Write(const void* buf, size_t count, size_t* bytes_written) override {
128 TEST_AND_RETURN_FALSE(writer_->Write(buf, count));
129 *bytes_written = count;
130 offset_ += count;
131 return true;
132 }
133
Seek(off_t pos)134 bool Seek(off_t pos) override {
135 if (reader_ != nullptr) {
136 TEST_AND_RETURN_FALSE(reader_->Seek(pos));
137 offset_ = pos;
138 } else {
139 // For writes technically there should be no change of position, or it
140 // should be equivalent of current offset.
141 TEST_AND_RETURN_FALSE(offset_ == static_cast<uint64_t>(pos));
142 }
143 return true;
144 }
145
Close()146 bool Close() override { return true; }
147
GetSize(uint64_t * size)148 bool GetSize(uint64_t* size) override {
149 *size = size_;
150 return true;
151 }
152
153 private:
BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,std::unique_ptr<ExtentWriter> writer,size_t size)154 BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,
155 std::unique_ptr<ExtentWriter> writer,
156 size_t size)
157 : reader_(std::move(reader)),
158 writer_(std::move(writer)),
159 size_(size),
160 offset_(0) {}
161
162 std::unique_ptr<ExtentReader> reader_;
163 std::unique_ptr<ExtentWriter> writer_;
164 uint64_t size_;
165 uint64_t offset_;
166
167 DISALLOW_COPY_AND_ASSIGN(BsdiffExtentFile);
168 };
169 // A class to be passed to |puffpatch| for reading from |source_fd_| and writing
170 // into |target_fd_|.
171 class PuffinExtentStream : public puffin::StreamInterface {
172 public:
173 // Constructor for creating a stream for reading from an |ExtentReader|.
PuffinExtentStream(std::unique_ptr<ExtentReader> reader,uint64_t size)174 PuffinExtentStream(std::unique_ptr<ExtentReader> reader, uint64_t size)
175 : PuffinExtentStream(std::move(reader), nullptr, size) {}
176
177 // Constructor for creating a stream for writing to an |ExtentWriter|.
PuffinExtentStream(std::unique_ptr<ExtentWriter> writer,uint64_t size)178 PuffinExtentStream(std::unique_ptr<ExtentWriter> writer, uint64_t size)
179 : PuffinExtentStream(nullptr, std::move(writer), size) {}
180
181 ~PuffinExtentStream() override = default;
182
GetSize(uint64_t * size) const183 bool GetSize(uint64_t* size) const override {
184 *size = size_;
185 return true;
186 }
187
GetOffset(uint64_t * offset) const188 bool GetOffset(uint64_t* offset) const override {
189 *offset = offset_;
190 return true;
191 }
192
Seek(uint64_t offset)193 bool Seek(uint64_t offset) override {
194 if (is_read_) {
195 TEST_AND_RETURN_FALSE(reader_->Seek(offset));
196 offset_ = offset;
197 } else {
198 // For writes technically there should be no change of position, or it
199 // should equivalent of current offset.
200 TEST_AND_RETURN_FALSE(offset_ == offset);
201 }
202 return true;
203 }
204
Read(void * buffer,size_t count)205 bool Read(void* buffer, size_t count) override {
206 TEST_AND_RETURN_FALSE(is_read_);
207 TEST_AND_RETURN_FALSE(reader_->Read(buffer, count));
208 offset_ += count;
209 return true;
210 }
211
Write(const void * buffer,size_t count)212 bool Write(const void* buffer, size_t count) override {
213 TEST_AND_RETURN_FALSE(!is_read_);
214 TEST_AND_RETURN_FALSE(writer_->Write(buffer, count));
215 offset_ += count;
216 return true;
217 }
218
Close()219 bool Close() override { return true; }
220
221 private:
PuffinExtentStream(std::unique_ptr<ExtentReader> reader,std::unique_ptr<ExtentWriter> writer,uint64_t size)222 PuffinExtentStream(std::unique_ptr<ExtentReader> reader,
223 std::unique_ptr<ExtentWriter> writer,
224 uint64_t size)
225 : reader_(std::move(reader)),
226 writer_(std::move(writer)),
227 size_(size),
228 offset_(0),
229 is_read_(reader_ ? true : false) {}
230
231 std::unique_ptr<ExtentReader> reader_;
232 std::unique_ptr<ExtentWriter> writer_;
233 uint64_t size_;
234 uint64_t offset_;
235 bool is_read_;
236
237 DISALLOW_COPY_AND_ASSIGN(PuffinExtentStream);
238 };
239
PartitionWriter(const PartitionUpdate & partition_update,const InstallPlan::Partition & install_part,DynamicPartitionControlInterface * dynamic_control,size_t block_size,bool is_interactive)240 PartitionWriter::PartitionWriter(
241 const PartitionUpdate& partition_update,
242 const InstallPlan::Partition& install_part,
243 DynamicPartitionControlInterface* dynamic_control,
244 size_t block_size,
245 bool is_interactive)
246 : partition_update_(partition_update),
247 install_part_(install_part),
248 dynamic_control_(dynamic_control),
249 interactive_(is_interactive),
250 block_size_(block_size) {}
251
~PartitionWriter()252 PartitionWriter::~PartitionWriter() {
253 Close();
254 }
255
OpenSourcePartition(uint32_t source_slot,bool source_may_exist)256 bool PartitionWriter::OpenSourcePartition(uint32_t source_slot,
257 bool source_may_exist) {
258 source_path_.clear();
259 if (!source_may_exist) {
260 return true;
261 }
262 if (install_part_.source_size > 0 && !install_part_.source_path.empty()) {
263 source_path_ = install_part_.source_path;
264 int err;
265 source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
266 if (source_fd_ == nullptr) {
267 LOG(ERROR) << "Unable to open source partition " << install_part_.name
268 << " on slot " << BootControlInterface::SlotName(source_slot)
269 << ", file " << source_path_;
270 return false;
271 }
272 }
273 return true;
274 }
275
Init(const InstallPlan * install_plan,bool source_may_exist,size_t next_op_index)276 bool PartitionWriter::Init(const InstallPlan* install_plan,
277 bool source_may_exist,
278 size_t next_op_index) {
279 const PartitionUpdate& partition = partition_update_;
280 uint32_t source_slot = install_plan->source_slot;
281 uint32_t target_slot = install_plan->target_slot;
282 TEST_AND_RETURN_FALSE(OpenSourcePartition(source_slot, source_may_exist));
283
284 // We shouldn't open the source partition in certain cases, e.g. some dynamic
285 // partitions in delta payload, partitions included in the full payload for
286 // partial updates. Use the source size as the indicator.
287
288 target_path_ = install_part_.target_path;
289 int err;
290
291 int flags = O_RDWR;
292 if (!interactive_)
293 flags |= O_DSYNC;
294
295 LOG(INFO) << "Opening " << target_path_ << " partition with"
296 << (interactive_ ? "out" : "") << " O_DSYNC";
297
298 target_fd_ = OpenFile(target_path_.c_str(), flags, true, &err);
299 if (!target_fd_) {
300 LOG(ERROR) << "Unable to open target partition "
301 << partition.partition_name() << " on slot "
302 << BootControlInterface::SlotName(target_slot) << ", file "
303 << target_path_;
304 return false;
305 }
306
307 LOG(INFO) << "Applying " << partition.operations().size()
308 << " operations to partition \"" << partition.partition_name()
309 << "\"";
310
311 // Discard the end of the partition, but ignore failures.
312 DiscardPartitionTail(target_fd_, install_part_.target_size);
313
314 return true;
315 }
316
PerformReplaceOperation(const InstallOperation & operation,const void * data,size_t count)317 bool PartitionWriter::PerformReplaceOperation(const InstallOperation& operation,
318 const void* data,
319 size_t count) {
320 // Setup the ExtentWriter stack based on the operation type.
321 std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
322
323 if (operation.type() == InstallOperation::REPLACE_BZ) {
324 writer.reset(new BzipExtentWriter(std::move(writer)));
325 } else if (operation.type() == InstallOperation::REPLACE_XZ) {
326 writer.reset(new XzExtentWriter(std::move(writer)));
327 }
328
329 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
330 TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
331
332 return true;
333 }
334
PerformZeroOrDiscardOperation(const InstallOperation & operation)335 bool PartitionWriter::PerformZeroOrDiscardOperation(
336 const InstallOperation& operation) {
337 #ifdef BLKZEROOUT
338 bool attempt_ioctl = true;
339 int request =
340 (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
341 #else // !defined(BLKZEROOUT)
342 bool attempt_ioctl = false;
343 int request = 0;
344 #endif // !defined(BLKZEROOUT)
345
346 brillo::Blob zeros;
347 for (const Extent& extent : operation.dst_extents()) {
348 const uint64_t start = extent.start_block() * block_size_;
349 const uint64_t length = extent.num_blocks() * block_size_;
350 if (attempt_ioctl) {
351 int result = 0;
352 if (target_fd_->BlkIoctl(request, start, length, &result) && result == 0)
353 continue;
354 attempt_ioctl = false;
355 }
356 // In case of failure, we fall back to writing 0 to the selected region.
357 zeros.resize(16 * block_size_);
358 for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
359 uint64_t chunk_length =
360 std::min(length - offset, static_cast<uint64_t>(zeros.size()));
361 TEST_AND_RETURN_FALSE(utils::WriteAll(
362 target_fd_, zeros.data(), chunk_length, start + offset));
363 }
364 }
365 return true;
366 }
367
PerformSourceCopyOperation(const InstallOperation & operation,ErrorCode * error)368 bool PartitionWriter::PerformSourceCopyOperation(
369 const InstallOperation& operation, ErrorCode* error) {
370 TEST_AND_RETURN_FALSE(source_fd_ != nullptr);
371
372 // The device may optimize the SOURCE_COPY operation.
373 // Being this a device-specific optimization let DynamicPartitionController
374 // decide it the operation should be skipped.
375 const PartitionUpdate& partition = partition_update_;
376 const auto& partition_control = dynamic_control_;
377
378 InstallOperation buf;
379 const bool should_optimize = partition_control->OptimizeOperation(
380 partition.partition_name(), operation, &buf);
381 const InstallOperation& optimized = should_optimize ? buf : operation;
382
383 if (operation.has_src_sha256_hash()) {
384 bool read_ok;
385 brillo::Blob source_hash;
386 brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
387 operation.src_sha256_hash().end());
388
389 // We fall back to use the error corrected device if the hash of the raw
390 // device doesn't match or there was an error reading the source partition.
391 // Note that this code will also fall back if writing the target partition
392 // fails.
393 if (should_optimize) {
394 // Hash operation.src_extents(), then copy optimized.src_extents to
395 // optimized.dst_extents.
396 read_ok =
397 fd_utils::ReadAndHashExtents(
398 source_fd_, operation.src_extents(), block_size_, &source_hash) &&
399 fd_utils::CopyAndHashExtents(source_fd_,
400 optimized.src_extents(),
401 target_fd_,
402 optimized.dst_extents(),
403 block_size_,
404 nullptr /* skip hashing */);
405 } else {
406 read_ok = fd_utils::CopyAndHashExtents(source_fd_,
407 operation.src_extents(),
408 target_fd_,
409 operation.dst_extents(),
410 block_size_,
411 &source_hash);
412 }
413 if (read_ok && expected_source_hash == source_hash)
414 return true;
415 LOG(WARNING) << "Source hash from RAW device mismatched, attempting to "
416 "correct using ECC";
417 if (!OpenCurrentECCPartition()) {
418 // The following function call will return false since the source hash
419 // mismatches, but we still want to call it so it prints the appropriate
420 // log message.
421 return ValidateSourceHash(source_hash, operation, source_fd_, error);
422 }
423
424 LOG(WARNING) << "Source hash from RAW device mismatched: found "
425 << base::HexEncode(source_hash.data(), source_hash.size())
426 << ", expected "
427 << base::HexEncode(expected_source_hash.data(),
428 expected_source_hash.size());
429 if (should_optimize) {
430 TEST_AND_RETURN_FALSE(fd_utils::ReadAndHashExtents(
431 source_ecc_fd_, operation.src_extents(), block_size_, &source_hash));
432 TEST_AND_RETURN_FALSE(
433 fd_utils::CopyAndHashExtents(source_ecc_fd_,
434 optimized.src_extents(),
435 target_fd_,
436 optimized.dst_extents(),
437 block_size_,
438 nullptr /* skip hashing */));
439 } else {
440 TEST_AND_RETURN_FALSE(
441 fd_utils::CopyAndHashExtents(source_ecc_fd_,
442 operation.src_extents(),
443 target_fd_,
444 operation.dst_extents(),
445 block_size_,
446 &source_hash));
447 }
448 TEST_AND_RETURN_FALSE(
449 ValidateSourceHash(source_hash, operation, source_ecc_fd_, error));
450 // At this point reading from the error corrected device worked, but
451 // reading from the raw device failed, so this is considered a recovered
452 // failure.
453 source_ecc_recovered_failures_++;
454 } else {
455 // When the operation doesn't include a source hash, we attempt the error
456 // corrected device first since we can't verify the block in the raw device
457 // at this point, but we fall back to the raw device since the error
458 // corrected device can be shorter or not available.
459
460 if (OpenCurrentECCPartition() &&
461 fd_utils::CopyAndHashExtents(source_ecc_fd_,
462 optimized.src_extents(),
463 target_fd_,
464 optimized.dst_extents(),
465 block_size_,
466 nullptr)) {
467 return true;
468 }
469 TEST_AND_RETURN_FALSE(fd_utils::CopyAndHashExtents(source_fd_,
470 optimized.src_extents(),
471 target_fd_,
472 optimized.dst_extents(),
473 block_size_,
474 nullptr));
475 }
476 return true;
477 }
478
PerformSourceBsdiffOperation(const InstallOperation & operation,ErrorCode * error,const void * data,size_t count)479 bool PartitionWriter::PerformSourceBsdiffOperation(
480 const InstallOperation& operation,
481 ErrorCode* error,
482 const void* data,
483 size_t count) {
484 FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
485 TEST_AND_RETURN_FALSE(source_fd != nullptr);
486
487 auto reader = std::make_unique<DirectExtentReader>();
488 TEST_AND_RETURN_FALSE(
489 reader->Init(source_fd, operation.src_extents(), block_size_));
490 auto src_file = std::make_unique<BsdiffExtentFile>(
491 std::move(reader),
492 utils::BlocksInExtents(operation.src_extents()) * block_size_);
493
494 auto writer = CreateBaseExtentWriter();
495 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
496 auto dst_file = std::make_unique<BsdiffExtentFile>(
497 std::move(writer),
498 utils::BlocksInExtents(operation.dst_extents()) * block_size_);
499
500 TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
501 std::move(dst_file),
502 reinterpret_cast<const uint8_t*>(data),
503 count) == 0);
504 return true;
505 }
506
PerformPuffDiffOperation(const InstallOperation & operation,ErrorCode * error,const void * data,size_t count)507 bool PartitionWriter::PerformPuffDiffOperation(
508 const InstallOperation& operation,
509 ErrorCode* error,
510 const void* data,
511 size_t count) {
512 FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
513 TEST_AND_RETURN_FALSE(source_fd != nullptr);
514
515 auto reader = std::make_unique<DirectExtentReader>();
516 TEST_AND_RETURN_FALSE(
517 reader->Init(source_fd, operation.src_extents(), block_size_));
518 puffin::UniqueStreamPtr src_stream(new PuffinExtentStream(
519 std::move(reader),
520 utils::BlocksInExtents(operation.src_extents()) * block_size_));
521
522 auto writer = CreateBaseExtentWriter();
523 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
524 puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
525 std::move(writer),
526 utils::BlocksInExtents(operation.dst_extents()) * block_size_));
527
528 constexpr size_t kMaxCacheSize = 5 * 1024 * 1024; // Total 5MB cache.
529 TEST_AND_RETURN_FALSE(
530 puffin::PuffPatch(std::move(src_stream),
531 std::move(dst_stream),
532 reinterpret_cast<const uint8_t*>(data),
533 count,
534 kMaxCacheSize));
535 return true;
536 }
537
ChooseSourceFD(const InstallOperation & operation,ErrorCode * error)538 FileDescriptorPtr PartitionWriter::ChooseSourceFD(
539 const InstallOperation& operation, ErrorCode* error) {
540 if (source_fd_ == nullptr) {
541 LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
542 return nullptr;
543 }
544
545 if (!operation.has_src_sha256_hash()) {
546 // When the operation doesn't include a source hash, we attempt the error
547 // corrected device first since we can't verify the block in the raw device
548 // at this point, but we first need to make sure all extents are readable
549 // since the error corrected device can be shorter or not available.
550 if (OpenCurrentECCPartition() &&
551 fd_utils::ReadAndHashExtents(
552 source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
553 return source_ecc_fd_;
554 }
555 return source_fd_;
556 }
557
558 brillo::Blob source_hash;
559 brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
560 operation.src_sha256_hash().end());
561 if (fd_utils::ReadAndHashExtents(
562 source_fd_, operation.src_extents(), block_size_, &source_hash) &&
563 source_hash == expected_source_hash) {
564 return source_fd_;
565 }
566 // We fall back to use the error corrected device if the hash of the raw
567 // device doesn't match or there was an error reading the source partition.
568 if (!OpenCurrentECCPartition()) {
569 // The following function call will return false since the source hash
570 // mismatches, but we still want to call it so it prints the appropriate
571 // log message.
572 ValidateSourceHash(source_hash, operation, source_fd_, error);
573 return nullptr;
574 }
575 LOG(WARNING) << "Source hash from RAW device mismatched: found "
576 << base::HexEncode(source_hash.data(), source_hash.size())
577 << ", expected "
578 << base::HexEncode(expected_source_hash.data(),
579 expected_source_hash.size());
580
581 if (fd_utils::ReadAndHashExtents(
582 source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
583 ValidateSourceHash(source_hash, operation, source_ecc_fd_, error)) {
584 // At this point reading from the error corrected device worked, but
585 // reading from the raw device failed, so this is considered a recovered
586 // failure.
587 source_ecc_recovered_failures_++;
588 return source_ecc_fd_;
589 }
590 return nullptr;
591 }
592
OpenCurrentECCPartition()593 bool PartitionWriter::OpenCurrentECCPartition() {
594 // No support for ECC for full payloads.
595 // Full payload should not have any opeartion that requires ECC partitions.
596 if (source_ecc_fd_)
597 return true;
598
599 if (source_ecc_open_failure_)
600 return false;
601
602 #if USE_FEC
603 const PartitionUpdate& partition = partition_update_;
604 const InstallPlan::Partition& install_part = install_part_;
605 std::string path = install_part.source_path;
606 FileDescriptorPtr fd(new FecFileDescriptor());
607 if (!fd->Open(path.c_str(), O_RDONLY, 0)) {
608 PLOG(ERROR) << "Unable to open ECC source partition "
609 << partition.partition_name() << ", file " << path;
610 source_ecc_open_failure_ = true;
611 return false;
612 }
613 source_ecc_fd_ = fd;
614 #else
615 // No support for ECC compiled.
616 source_ecc_open_failure_ = true;
617 #endif // USE_FEC
618
619 return !source_ecc_open_failure_;
620 }
621
Close()622 int PartitionWriter::Close() {
623 int err = 0;
624 if (source_fd_ && !source_fd_->Close()) {
625 err = errno;
626 PLOG(ERROR) << "Error closing source partition";
627 if (!err)
628 err = 1;
629 }
630 source_fd_.reset();
631 source_path_.clear();
632
633 if (target_fd_ && !target_fd_->Close()) {
634 err = errno;
635 PLOG(ERROR) << "Error closing target partition";
636 if (!err)
637 err = 1;
638 }
639 target_fd_.reset();
640 target_path_.clear();
641
642 if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
643 err = errno;
644 PLOG(ERROR) << "Error closing ECC source partition";
645 if (!err)
646 err = 1;
647 }
648 source_ecc_fd_.reset();
649 source_ecc_open_failure_ = false;
650 return -err;
651 }
652
CheckpointUpdateProgress(size_t next_op_index)653 void PartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
654 target_fd_->Flush();
655 }
656
CreateBaseExtentWriter()657 std::unique_ptr<ExtentWriter> PartitionWriter::CreateBaseExtentWriter() {
658 return std::make_unique<DirectExtentWriter>(target_fd_);
659 }
660
661 } // namespace chromeos_update_engine
662