• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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