blob: 12b206e4135b41530a6ac7f00b9ac1b9dfc22dcb [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>
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
46namespace chromeos_update_engine {
47
48namespace {
49constexpr 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
55bool 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.
88FileDescriptorPtr 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
111class BsdiffExtentFile : public bsdiff::FileInterface {
112 public:
113 BsdiffExtentFile(std::unique_ptr<ExtentReader> reader, size_t size)
114 : BsdiffExtentFile(std::move(reader), nullptr, size) {}
115 BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer, size_t size)
116 : BsdiffExtentFile(nullptr, std::move(writer), size) {}
117
118 ~BsdiffExtentFile() override = default;
119
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
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
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
146 bool Close() override { return true; }
147
148 bool GetSize(uint64_t* size) override {
149 *size = size_;
150 return true;
151 }
152
153 private:
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_|.
171class PuffinExtentStream : public puffin::StreamInterface {
172 public:
173 // Constructor for creating a stream for reading from an |ExtentReader|.
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|.
178 PuffinExtentStream(std::unique_ptr<ExtentWriter> writer, uint64_t size)
179 : PuffinExtentStream(nullptr, std::move(writer), size) {}
180
181 ~PuffinExtentStream() override = default;
182
183 bool GetSize(uint64_t* size) const override {
184 *size = size_;
185 return true;
186 }
187
188 bool GetOffset(uint64_t* offset) const override {
189 *offset = offset_;
190 return true;
191 }
192
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
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
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
219 bool Close() override { return true; }
220
221 private:
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
240PartitionWriter::PartitionWriter(
241 const PartitionUpdate& partition_update,
242 const InstallPlan::Partition& install_part,
243 DynamicPartitionControlInterface* dynamic_control,
244 size_t block_size,
Kelvin Zhang24599af2020-10-27 13:44:25 -0400245 PrefsInterface* prefs,
Kelvin Zhang50bac652020-09-28 15:51:41 -0400246 bool is_interactive)
247 : partition_update_(partition_update),
248 install_part_(install_part),
249 dynamic_control_(dynamic_control),
250 interactive_(is_interactive),
Kelvin Zhang24599af2020-10-27 13:44:25 -0400251 block_size_(block_size),
252 prefs_(prefs) {}
Kelvin Zhang50bac652020-09-28 15:51:41 -0400253
254PartitionWriter::~PartitionWriter() {
255 Close();
256}
257
258bool PartitionWriter::Init(const InstallPlan* install_plan,
259 bool source_may_exist) {
260 const PartitionUpdate& partition = partition_update_;
261 uint32_t source_slot = install_plan->source_slot;
262 uint32_t target_slot = install_plan->target_slot;
263
264 // We shouldn't open the source partition in certain cases, e.g. some dynamic
265 // partitions in delta payload, partitions included in the full payload for
266 // partial updates. Use the source size as the indicator.
267 if (source_may_exist && install_part_.source_size > 0) {
268 source_path_ = install_part_.source_path;
269 int err;
270 source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
271 if (!source_fd_) {
272 LOG(ERROR) << "Unable to open source partition "
273 << partition.partition_name() << " on slot "
274 << BootControlInterface::SlotName(source_slot) << ", file "
275 << source_path_;
276 return false;
277 }
278 }
279
280 target_path_ = install_part_.target_path;
281 int err;
282
283 int flags = O_RDWR;
284 if (!interactive_)
285 flags |= O_DSYNC;
286
287 LOG(INFO) << "Opening " << target_path_ << " partition with"
288 << (interactive_ ? "out" : "") << " O_DSYNC";
289
290 target_fd_ = OpenFile(target_path_.c_str(), flags, true, &err);
291 if (!target_fd_) {
292 LOG(ERROR) << "Unable to open target partition "
293 << partition.partition_name() << " on slot "
294 << BootControlInterface::SlotName(target_slot) << ", file "
295 << target_path_;
296 return false;
297 }
298
299 LOG(INFO) << "Applying " << partition.operations().size()
300 << " operations to partition \"" << partition.partition_name()
301 << "\"";
302
303 // Discard the end of the partition, but ignore failures.
304 DiscardPartitionTail(target_fd_, install_part_.target_size);
305
306 return true;
307}
308
309bool PartitionWriter::PerformReplaceOperation(const InstallOperation& operation,
310 const void* data,
311 size_t count) {
312 // Setup the ExtentWriter stack based on the operation type.
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400313 std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400314
315 if (operation.type() == InstallOperation::REPLACE_BZ) {
316 writer.reset(new BzipExtentWriter(std::move(writer)));
317 } else if (operation.type() == InstallOperation::REPLACE_XZ) {
318 writer.reset(new XzExtentWriter(std::move(writer)));
319 }
320
321 TEST_AND_RETURN_FALSE(
322 writer->Init(target_fd_, operation.dst_extents(), block_size_));
323 TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
324
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400325 return Flush();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400326}
327
328bool PartitionWriter::PerformZeroOrDiscardOperation(
329 const InstallOperation& operation) {
330#ifdef BLKZEROOUT
331 bool attempt_ioctl = true;
332 int request =
333 (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
334#else // !defined(BLKZEROOUT)
335 bool attempt_ioctl = false;
336 int request = 0;
337#endif // !defined(BLKZEROOUT)
338
339 brillo::Blob zeros;
340 for (const Extent& extent : operation.dst_extents()) {
341 const uint64_t start = extent.start_block() * block_size_;
342 const uint64_t length = extent.num_blocks() * block_size_;
343 if (attempt_ioctl) {
344 int result = 0;
345 if (target_fd_->BlkIoctl(request, start, length, &result) && result == 0)
346 continue;
347 attempt_ioctl = false;
348 }
349 // In case of failure, we fall back to writing 0 to the selected region.
350 zeros.resize(16 * block_size_);
351 for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
352 uint64_t chunk_length =
353 std::min(length - offset, static_cast<uint64_t>(zeros.size()));
Kelvin Zhang4b280242020-11-06 16:07:45 -0500354 TEST_AND_RETURN_FALSE(utils::WriteAll(
Kelvin Zhang50bac652020-09-28 15:51:41 -0400355 target_fd_, zeros.data(), chunk_length, start + offset));
356 }
357 }
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400358 return Flush();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400359}
360
361bool PartitionWriter::PerformSourceCopyOperation(
362 const InstallOperation& operation, ErrorCode* error) {
363 TEST_AND_RETURN_FALSE(source_fd_ != nullptr);
364
365 // The device may optimize the SOURCE_COPY operation.
366 // Being this a device-specific optimization let DynamicPartitionController
367 // decide it the operation should be skipped.
368 const PartitionUpdate& partition = partition_update_;
369 const auto& partition_control = dynamic_control_;
370
371 InstallOperation buf;
372 bool should_optimize = partition_control->OptimizeOperation(
373 partition.partition_name(), operation, &buf);
374 const InstallOperation& optimized = should_optimize ? buf : operation;
375
376 if (operation.has_src_sha256_hash()) {
377 bool read_ok;
378 brillo::Blob source_hash;
379 brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
380 operation.src_sha256_hash().end());
381
382 // We fall back to use the error corrected device if the hash of the raw
383 // device doesn't match or there was an error reading the source partition.
384 // Note that this code will also fall back if writing the target partition
385 // fails.
386 if (should_optimize) {
387 // Hash operation.src_extents(), then copy optimized.src_extents to
388 // optimized.dst_extents.
389 read_ok =
390 fd_utils::ReadAndHashExtents(
391 source_fd_, operation.src_extents(), block_size_, &source_hash) &&
392 fd_utils::CopyAndHashExtents(source_fd_,
393 optimized.src_extents(),
394 target_fd_,
395 optimized.dst_extents(),
396 block_size_,
397 nullptr /* skip hashing */);
398 } else {
399 read_ok = fd_utils::CopyAndHashExtents(source_fd_,
400 operation.src_extents(),
401 target_fd_,
402 operation.dst_extents(),
403 block_size_,
404 &source_hash);
405 }
406 if (read_ok && expected_source_hash == source_hash)
407 return true;
408 LOG(WARNING) << "Source hash from RAW device mismatched, attempting to "
409 "correct using ECC";
410 if (!OpenCurrentECCPartition()) {
411 // The following function call will return false since the source hash
412 // mismatches, but we still want to call it so it prints the appropriate
413 // log message.
414 return ValidateSourceHash(source_hash, operation, source_fd_, error);
415 }
416
417 LOG(WARNING) << "Source hash from RAW device mismatched: found "
418 << base::HexEncode(source_hash.data(), source_hash.size())
419 << ", expected "
420 << base::HexEncode(expected_source_hash.data(),
421 expected_source_hash.size());
422 if (should_optimize) {
423 TEST_AND_RETURN_FALSE(fd_utils::ReadAndHashExtents(
424 source_ecc_fd_, operation.src_extents(), block_size_, &source_hash));
425 TEST_AND_RETURN_FALSE(
426 fd_utils::CopyAndHashExtents(source_ecc_fd_,
427 optimized.src_extents(),
428 target_fd_,
429 optimized.dst_extents(),
430 block_size_,
431 nullptr /* skip hashing */));
432 } else {
433 TEST_AND_RETURN_FALSE(
434 fd_utils::CopyAndHashExtents(source_ecc_fd_,
435 operation.src_extents(),
436 target_fd_,
437 operation.dst_extents(),
438 block_size_,
439 &source_hash));
440 }
441 TEST_AND_RETURN_FALSE(
442 ValidateSourceHash(source_hash, operation, source_ecc_fd_, error));
443 // At this point reading from the error corrected device worked, but
444 // reading from the raw device failed, so this is considered a recovered
445 // failure.
446 source_ecc_recovered_failures_++;
447 } else {
448 // When the operation doesn't include a source hash, we attempt the error
449 // corrected device first since we can't verify the block in the raw device
450 // at this point, but we fall back to the raw device since the error
451 // corrected device can be shorter or not available.
452
453 if (OpenCurrentECCPartition() &&
454 fd_utils::CopyAndHashExtents(source_ecc_fd_,
455 optimized.src_extents(),
456 target_fd_,
457 optimized.dst_extents(),
458 block_size_,
459 nullptr)) {
460 return true;
461 }
462 TEST_AND_RETURN_FALSE(fd_utils::CopyAndHashExtents(source_fd_,
463 optimized.src_extents(),
464 target_fd_,
465 optimized.dst_extents(),
466 block_size_,
467 nullptr));
468 }
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400469 return Flush();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400470}
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400471
Kelvin Zhang50bac652020-09-28 15:51:41 -0400472bool PartitionWriter::PerformSourceBsdiffOperation(
473 const InstallOperation& operation,
474 ErrorCode* error,
475 const void* data,
476 size_t count) {
477 FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
478 TEST_AND_RETURN_FALSE(source_fd != nullptr);
479
480 auto reader = std::make_unique<DirectExtentReader>();
481 TEST_AND_RETURN_FALSE(
482 reader->Init(source_fd, operation.src_extents(), block_size_));
483 auto src_file = std::make_unique<BsdiffExtentFile>(
484 std::move(reader),
485 utils::BlocksInExtents(operation.src_extents()) * block_size_);
486
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400487 auto writer = CreateBaseExtentWriter();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400488 TEST_AND_RETURN_FALSE(
489 writer->Init(target_fd_, operation.dst_extents(), block_size_));
490 auto dst_file = std::make_unique<BsdiffExtentFile>(
491 std::move(writer),
492 utils::BlocksInExtents(operation.dst_extents()) * block_size_);
493
494 TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
495 std::move(dst_file),
496 reinterpret_cast<const uint8_t*>(data),
497 count) == 0);
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400498 return Flush();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400499}
500
501bool PartitionWriter::PerformPuffDiffOperation(
502 const InstallOperation& operation,
503 ErrorCode* error,
504 const void* data,
505 size_t count) {
506 FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
507 TEST_AND_RETURN_FALSE(source_fd != nullptr);
508
509 auto reader = std::make_unique<DirectExtentReader>();
510 TEST_AND_RETURN_FALSE(
511 reader->Init(source_fd, operation.src_extents(), block_size_));
512 puffin::UniqueStreamPtr src_stream(new PuffinExtentStream(
513 std::move(reader),
514 utils::BlocksInExtents(operation.src_extents()) * block_size_));
515
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400516 auto writer = CreateBaseExtentWriter();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400517 TEST_AND_RETURN_FALSE(
518 writer->Init(target_fd_, operation.dst_extents(), block_size_));
519 puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
520 std::move(writer),
521 utils::BlocksInExtents(operation.dst_extents()) * block_size_));
522
523 constexpr size_t kMaxCacheSize = 5 * 1024 * 1024; // Total 5MB cache.
524 TEST_AND_RETURN_FALSE(
525 puffin::PuffPatch(std::move(src_stream),
526 std::move(dst_stream),
527 reinterpret_cast<const uint8_t*>(data),
528 count,
529 kMaxCacheSize));
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400530 return Flush();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400531}
532
533FileDescriptorPtr PartitionWriter::ChooseSourceFD(
534 const InstallOperation& operation, ErrorCode* error) {
535 if (source_fd_ == nullptr) {
536 LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
537 return nullptr;
538 }
539
540 if (!operation.has_src_sha256_hash()) {
541 // When the operation doesn't include a source hash, we attempt the error
542 // corrected device first since we can't verify the block in the raw device
543 // at this point, but we first need to make sure all extents are readable
544 // since the error corrected device can be shorter or not available.
545 if (OpenCurrentECCPartition() &&
546 fd_utils::ReadAndHashExtents(
547 source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
548 return source_ecc_fd_;
549 }
550 return source_fd_;
551 }
552
553 brillo::Blob source_hash;
554 brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
555 operation.src_sha256_hash().end());
556 if (fd_utils::ReadAndHashExtents(
557 source_fd_, operation.src_extents(), block_size_, &source_hash) &&
558 source_hash == expected_source_hash) {
559 return source_fd_;
560 }
561 // We fall back to use the error corrected device if the hash of the raw
562 // device doesn't match or there was an error reading the source partition.
563 if (!OpenCurrentECCPartition()) {
564 // The following function call will return false since the source hash
565 // mismatches, but we still want to call it so it prints the appropriate
566 // log message.
567 ValidateSourceHash(source_hash, operation, source_fd_, error);
568 return nullptr;
569 }
570 LOG(WARNING) << "Source hash from RAW device mismatched: found "
571 << base::HexEncode(source_hash.data(), source_hash.size())
572 << ", expected "
573 << base::HexEncode(expected_source_hash.data(),
574 expected_source_hash.size());
575
576 if (fd_utils::ReadAndHashExtents(
577 source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
578 ValidateSourceHash(source_hash, operation, source_ecc_fd_, error)) {
579 // At this point reading from the error corrected device worked, but
580 // reading from the raw device failed, so this is considered a recovered
581 // failure.
582 source_ecc_recovered_failures_++;
583 return source_ecc_fd_;
584 }
585 return nullptr;
586}
587
588bool PartitionWriter::OpenCurrentECCPartition() {
589 // No support for ECC for full payloads.
590 // Full payload should not have any opeartion that requires ECC partitions.
591 if (source_ecc_fd_)
592 return true;
593
594 if (source_ecc_open_failure_)
595 return false;
596
597#if USE_FEC
598 const PartitionUpdate& partition = partition_update_;
599 const InstallPlan::Partition& install_part = install_part_;
600 std::string path = install_part.source_path;
601 FileDescriptorPtr fd(new FecFileDescriptor());
602 if (!fd->Open(path.c_str(), O_RDONLY, 0)) {
603 PLOG(ERROR) << "Unable to open ECC source partition "
604 << partition.partition_name() << ", file " << path;
605 source_ecc_open_failure_ = true;
606 return false;
607 }
608 source_ecc_fd_ = fd;
609#else
610 // No support for ECC compiled.
611 source_ecc_open_failure_ = true;
612#endif // USE_FEC
613
614 return !source_ecc_open_failure_;
615}
616
617int PartitionWriter::Close() {
618 int err = 0;
619 if (source_fd_ && !source_fd_->Close()) {
620 err = errno;
621 PLOG(ERROR) << "Error closing source partition";
622 if (!err)
623 err = 1;
624 }
625 source_fd_.reset();
626 source_path_.clear();
627
628 if (target_fd_ && !target_fd_->Close()) {
629 err = errno;
630 PLOG(ERROR) << "Error closing target partition";
631 if (!err)
632 err = 1;
633 }
634 target_fd_.reset();
635 target_path_.clear();
636
637 if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
638 err = errno;
639 PLOG(ERROR) << "Error closing ECC source partition";
640 if (!err)
641 err = 1;
642 }
643 source_ecc_fd_.reset();
644 source_ecc_open_failure_ = false;
645 return -err;
646}
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400647
648std::unique_ptr<ExtentWriter> PartitionWriter::CreateBaseExtentWriter() {
649 return std::make_unique<DirectExtentWriter>();
650}
651
652bool PartitionWriter::Flush() {
653 return target_fd_->Flush();
654}
655
Kelvin Zhang50bac652020-09-28 15:51:41 -0400656} // namespace chromeos_update_engine