blob: f2022a18678cd2de13fa677e20a3fa993925d160 [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"
Kelvin Zhang37b9b702021-02-23 10:30:37 -050045#include "update_engine/payload_generator/extent_utils.h"
Kelvin Zhang50bac652020-09-28 15:51:41 -040046
47namespace chromeos_update_engine {
48
49namespace {
50constexpr uint64_t kCacheSize = 1024 * 1024; // 1MB
51
52// Discard the tail of the block device referenced by |fd|, from the offset
53// |data_size| until the end of the block device. Returns whether the data was
54// discarded.
55
56bool DiscardPartitionTail(const FileDescriptorPtr& fd, uint64_t data_size) {
57 uint64_t part_size = fd->BlockDevSize();
58 if (!part_size || part_size <= data_size)
59 return false;
60
61 struct blkioctl_request {
62 int number;
63 const char* name;
64 };
65 const std::initializer_list<blkioctl_request> blkioctl_requests = {
66 {BLKDISCARD, "BLKDISCARD"},
67 {BLKSECDISCARD, "BLKSECDISCARD"},
68#ifdef BLKZEROOUT
69 {BLKZEROOUT, "BLKZEROOUT"},
70#endif
71 };
72 for (const auto& req : blkioctl_requests) {
73 int error = 0;
74 if (fd->BlkIoctl(req.number, data_size, part_size - data_size, &error) &&
75 error == 0) {
76 return true;
77 }
78 LOG(WARNING) << "Error discarding the last "
79 << (part_size - data_size) / 1024 << " KiB using ioctl("
80 << req.name << ")";
81 }
82 return false;
83}
84
85} // namespace
86
Kelvin Zhang37b9b702021-02-23 10:30:37 -050087using google::protobuf::RepeatedPtrField;
88
Kelvin Zhang50bac652020-09-28 15:51:41 -040089// Opens path for read/write. On success returns an open FileDescriptor
90// and sets *err to 0. On failure, sets *err to errno and returns nullptr.
91FileDescriptorPtr OpenFile(const char* path,
92 int mode,
93 bool cache_writes,
94 int* err) {
95 // Try to mark the block device read-only based on the mode. Ignore any
96 // failure since this won't work when passing regular files.
97 bool read_only = (mode & O_ACCMODE) == O_RDONLY;
98 utils::SetBlockDeviceReadOnly(path, read_only);
99
100 FileDescriptorPtr fd(new EintrSafeFileDescriptor());
101 if (cache_writes && !read_only) {
102 fd = FileDescriptorPtr(new CachedFileDescriptor(fd, kCacheSize));
103 LOG(INFO) << "Caching writes.";
104 }
105 if (!fd->Open(path, mode, 000)) {
106 *err = errno;
107 PLOG(ERROR) << "Unable to open file " << path;
108 return nullptr;
109 }
110 *err = 0;
111 return fd;
112}
113
114class BsdiffExtentFile : public bsdiff::FileInterface {
115 public:
116 BsdiffExtentFile(std::unique_ptr<ExtentReader> reader, size_t size)
117 : BsdiffExtentFile(std::move(reader), nullptr, size) {}
118 BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer, size_t size)
119 : BsdiffExtentFile(nullptr, std::move(writer), size) {}
120
121 ~BsdiffExtentFile() override = default;
122
123 bool Read(void* buf, size_t count, size_t* bytes_read) override {
124 TEST_AND_RETURN_FALSE(reader_->Read(buf, count));
125 *bytes_read = count;
126 offset_ += count;
127 return true;
128 }
129
130 bool Write(const void* buf, size_t count, size_t* bytes_written) override {
131 TEST_AND_RETURN_FALSE(writer_->Write(buf, count));
132 *bytes_written = count;
133 offset_ += count;
134 return true;
135 }
136
137 bool Seek(off_t pos) override {
138 if (reader_ != nullptr) {
139 TEST_AND_RETURN_FALSE(reader_->Seek(pos));
140 offset_ = pos;
141 } else {
142 // For writes technically there should be no change of position, or it
143 // should be equivalent of current offset.
144 TEST_AND_RETURN_FALSE(offset_ == static_cast<uint64_t>(pos));
145 }
146 return true;
147 }
148
149 bool Close() override { return true; }
150
151 bool GetSize(uint64_t* size) override {
152 *size = size_;
153 return true;
154 }
155
156 private:
157 BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,
158 std::unique_ptr<ExtentWriter> writer,
159 size_t size)
160 : reader_(std::move(reader)),
161 writer_(std::move(writer)),
162 size_(size),
163 offset_(0) {}
164
165 std::unique_ptr<ExtentReader> reader_;
166 std::unique_ptr<ExtentWriter> writer_;
167 uint64_t size_;
168 uint64_t offset_;
169
170 DISALLOW_COPY_AND_ASSIGN(BsdiffExtentFile);
171};
172// A class to be passed to |puffpatch| for reading from |source_fd_| and writing
173// into |target_fd_|.
174class PuffinExtentStream : public puffin::StreamInterface {
175 public:
176 // Constructor for creating a stream for reading from an |ExtentReader|.
177 PuffinExtentStream(std::unique_ptr<ExtentReader> reader, uint64_t size)
178 : PuffinExtentStream(std::move(reader), nullptr, size) {}
179
180 // Constructor for creating a stream for writing to an |ExtentWriter|.
181 PuffinExtentStream(std::unique_ptr<ExtentWriter> writer, uint64_t size)
182 : PuffinExtentStream(nullptr, std::move(writer), size) {}
183
184 ~PuffinExtentStream() override = default;
185
186 bool GetSize(uint64_t* size) const override {
187 *size = size_;
188 return true;
189 }
190
191 bool GetOffset(uint64_t* offset) const override {
192 *offset = offset_;
193 return true;
194 }
195
196 bool Seek(uint64_t offset) override {
197 if (is_read_) {
198 TEST_AND_RETURN_FALSE(reader_->Seek(offset));
199 offset_ = offset;
200 } else {
201 // For writes technically there should be no change of position, or it
202 // should equivalent of current offset.
203 TEST_AND_RETURN_FALSE(offset_ == offset);
204 }
205 return true;
206 }
207
208 bool Read(void* buffer, size_t count) override {
209 TEST_AND_RETURN_FALSE(is_read_);
210 TEST_AND_RETURN_FALSE(reader_->Read(buffer, count));
211 offset_ += count;
212 return true;
213 }
214
215 bool Write(const void* buffer, size_t count) override {
216 TEST_AND_RETURN_FALSE(!is_read_);
217 TEST_AND_RETURN_FALSE(writer_->Write(buffer, count));
218 offset_ += count;
219 return true;
220 }
221
222 bool Close() override { return true; }
223
224 private:
225 PuffinExtentStream(std::unique_ptr<ExtentReader> reader,
226 std::unique_ptr<ExtentWriter> writer,
227 uint64_t size)
228 : reader_(std::move(reader)),
229 writer_(std::move(writer)),
230 size_(size),
231 offset_(0),
232 is_read_(reader_ ? true : false) {}
233
234 std::unique_ptr<ExtentReader> reader_;
235 std::unique_ptr<ExtentWriter> writer_;
236 uint64_t size_;
237 uint64_t offset_;
238 bool is_read_;
239
240 DISALLOW_COPY_AND_ASSIGN(PuffinExtentStream);
241};
242
243PartitionWriter::PartitionWriter(
244 const PartitionUpdate& partition_update,
245 const InstallPlan::Partition& install_part,
246 DynamicPartitionControlInterface* dynamic_control,
247 size_t block_size,
248 bool is_interactive)
249 : partition_update_(partition_update),
250 install_part_(install_part),
251 dynamic_control_(dynamic_control),
252 interactive_(is_interactive),
Kelvin Zhang59928f12020-11-11 21:21:27 +0000253 block_size_(block_size) {}
Kelvin Zhang50bac652020-09-28 15:51:41 -0400254
255PartitionWriter::~PartitionWriter() {
256 Close();
257}
258
Kelvin Zhang3f60d532020-11-09 13:33:17 -0500259bool PartitionWriter::OpenSourcePartition(uint32_t source_slot,
260 bool source_may_exist) {
261 source_path_.clear();
262 if (!source_may_exist) {
263 return true;
264 }
265 if (install_part_.source_size > 0 && !install_part_.source_path.empty()) {
266 source_path_ = install_part_.source_path;
267 int err;
268 source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
269 if (source_fd_ == nullptr) {
270 LOG(ERROR) << "Unable to open source partition " << install_part_.name
271 << " on slot " << BootControlInterface::SlotName(source_slot)
272 << ", file " << source_path_;
273 return false;
274 }
275 }
276 return true;
277}
278
Kelvin Zhang50bac652020-09-28 15:51:41 -0400279bool PartitionWriter::Init(const InstallPlan* install_plan,
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400280 bool source_may_exist,
281 size_t next_op_index) {
Kelvin Zhang50bac652020-09-28 15:51:41 -0400282 const PartitionUpdate& partition = partition_update_;
283 uint32_t source_slot = install_plan->source_slot;
284 uint32_t target_slot = install_plan->target_slot;
Kelvin Zhang3f60d532020-11-09 13:33:17 -0500285 TEST_AND_RETURN_FALSE(OpenSourcePartition(source_slot, source_may_exist));
Kelvin Zhang50bac652020-09-28 15:51:41 -0400286
287 // We shouldn't open the source partition in certain cases, e.g. some dynamic
288 // partitions in delta payload, partitions included in the full payload for
289 // partial updates. Use the source size as the indicator.
Kelvin Zhang50bac652020-09-28 15:51:41 -0400290
291 target_path_ = install_part_.target_path;
292 int err;
293
294 int flags = O_RDWR;
295 if (!interactive_)
296 flags |= O_DSYNC;
297
298 LOG(INFO) << "Opening " << target_path_ << " partition with"
299 << (interactive_ ? "out" : "") << " O_DSYNC";
300
301 target_fd_ = OpenFile(target_path_.c_str(), flags, true, &err);
302 if (!target_fd_) {
303 LOG(ERROR) << "Unable to open target partition "
304 << partition.partition_name() << " on slot "
305 << BootControlInterface::SlotName(target_slot) << ", file "
306 << target_path_;
307 return false;
308 }
309
310 LOG(INFO) << "Applying " << partition.operations().size()
311 << " operations to partition \"" << partition.partition_name()
312 << "\"";
313
314 // Discard the end of the partition, but ignore failures.
315 DiscardPartitionTail(target_fd_, install_part_.target_size);
316
317 return true;
318}
319
320bool PartitionWriter::PerformReplaceOperation(const InstallOperation& operation,
321 const void* data,
322 size_t count) {
323 // Setup the ExtentWriter stack based on the operation type.
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400324 std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
Kelvin Zhang50bac652020-09-28 15:51:41 -0400325
326 if (operation.type() == InstallOperation::REPLACE_BZ) {
327 writer.reset(new BzipExtentWriter(std::move(writer)));
328 } else if (operation.type() == InstallOperation::REPLACE_XZ) {
329 writer.reset(new XzExtentWriter(std::move(writer)));
330 }
331
Kelvin Zhang4d22ca22021-02-09 14:06:25 -0500332 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
Kelvin Zhang50bac652020-09-28 15:51:41 -0400333 TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
334
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400335 return true;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400336}
337
338bool PartitionWriter::PerformZeroOrDiscardOperation(
339 const InstallOperation& operation) {
340#ifdef BLKZEROOUT
341 bool attempt_ioctl = true;
342 int request =
343 (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
344#else // !defined(BLKZEROOUT)
345 bool attempt_ioctl = false;
346 int request = 0;
347#endif // !defined(BLKZEROOUT)
348
349 brillo::Blob zeros;
350 for (const Extent& extent : operation.dst_extents()) {
351 const uint64_t start = extent.start_block() * block_size_;
352 const uint64_t length = extent.num_blocks() * block_size_;
353 if (attempt_ioctl) {
354 int result = 0;
355 if (target_fd_->BlkIoctl(request, start, length, &result) && result == 0)
356 continue;
357 attempt_ioctl = false;
358 }
359 // In case of failure, we fall back to writing 0 to the selected region.
360 zeros.resize(16 * block_size_);
361 for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
362 uint64_t chunk_length =
363 std::min(length - offset, static_cast<uint64_t>(zeros.size()));
Kelvin Zhang4b280242020-11-06 16:07:45 -0500364 TEST_AND_RETURN_FALSE(utils::WriteAll(
Kelvin Zhang50bac652020-09-28 15:51:41 -0400365 target_fd_, zeros.data(), chunk_length, start + offset));
366 }
367 }
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400368 return true;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400369}
370
Kelvin Zhang37b9b702021-02-23 10:30:37 -0500371std::ostream& operator<<(std::ostream& out,
372 const RepeatedPtrField<Extent>& extents) {
373 if (extents.size() == 0) {
374 out << "[]";
375 return out;
376 }
377 out << "[";
378 auto begin = extents.begin();
379 out << *begin;
380 for (int i = 1; i < extents.size(); i++) {
381 ++begin;
382 out << ", " << *begin;
383 }
384 out << "]";
385 return out;
386}
387
Kelvin Zhang50bac652020-09-28 15:51:41 -0400388bool PartitionWriter::PerformSourceCopyOperation(
389 const InstallOperation& operation, ErrorCode* error) {
390 TEST_AND_RETURN_FALSE(source_fd_ != nullptr);
391
392 // The device may optimize the SOURCE_COPY operation.
393 // Being this a device-specific optimization let DynamicPartitionController
394 // decide it the operation should be skipped.
395 const PartitionUpdate& partition = partition_update_;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400396
397 InstallOperation buf;
Kelvin Zhang37b9b702021-02-23 10:30:37 -0500398 const bool should_optimize = dynamic_control_->OptimizeOperation(
Kelvin Zhang50bac652020-09-28 15:51:41 -0400399 partition.partition_name(), operation, &buf);
400 const InstallOperation& optimized = should_optimize ? buf : operation;
401
Kelvin Zhang37b9b702021-02-23 10:30:37 -0500402 // Invoke ChooseSourceFD with original operation, so that it can properly
403 // verify source hashes. Optimized operation might contain a smaller set of
404 // extents, or completely empty.
405 auto source_fd = ChooseSourceFD(operation, error);
406 if (source_fd == nullptr) {
407 LOG(ERROR) << "Unrecoverable source hash mismatch found on partition "
408 << partition.partition_name()
409 << " extents: " << operation.src_extents();
410 return false;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400411 }
Kelvin Zhang37b9b702021-02-23 10:30:37 -0500412
413 return fd_utils::CopyAndHashExtents(source_fd,
414 optimized.src_extents(),
415 target_fd_,
416 optimized.dst_extents(),
417 block_size_,
418 nullptr);
Kelvin Zhang50bac652020-09-28 15:51:41 -0400419}
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400420
Kelvin Zhang50bac652020-09-28 15:51:41 -0400421bool PartitionWriter::PerformSourceBsdiffOperation(
422 const InstallOperation& operation,
423 ErrorCode* error,
424 const void* data,
425 size_t count) {
426 FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
427 TEST_AND_RETURN_FALSE(source_fd != nullptr);
428
429 auto reader = std::make_unique<DirectExtentReader>();
430 TEST_AND_RETURN_FALSE(
431 reader->Init(source_fd, operation.src_extents(), block_size_));
432 auto src_file = std::make_unique<BsdiffExtentFile>(
433 std::move(reader),
434 utils::BlocksInExtents(operation.src_extents()) * block_size_);
435
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400436 auto writer = CreateBaseExtentWriter();
Kelvin Zhang4d22ca22021-02-09 14:06:25 -0500437 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
Kelvin Zhang50bac652020-09-28 15:51:41 -0400438 auto dst_file = std::make_unique<BsdiffExtentFile>(
439 std::move(writer),
440 utils::BlocksInExtents(operation.dst_extents()) * block_size_);
441
442 TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
443 std::move(dst_file),
444 reinterpret_cast<const uint8_t*>(data),
445 count) == 0);
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400446 return true;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400447}
448
449bool PartitionWriter::PerformPuffDiffOperation(
450 const InstallOperation& operation,
451 ErrorCode* error,
452 const void* data,
453 size_t count) {
454 FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
455 TEST_AND_RETURN_FALSE(source_fd != nullptr);
456
457 auto reader = std::make_unique<DirectExtentReader>();
458 TEST_AND_RETURN_FALSE(
459 reader->Init(source_fd, operation.src_extents(), block_size_));
460 puffin::UniqueStreamPtr src_stream(new PuffinExtentStream(
461 std::move(reader),
462 utils::BlocksInExtents(operation.src_extents()) * block_size_));
463
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400464 auto writer = CreateBaseExtentWriter();
Kelvin Zhang4d22ca22021-02-09 14:06:25 -0500465 TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
Kelvin Zhang50bac652020-09-28 15:51:41 -0400466 puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
467 std::move(writer),
468 utils::BlocksInExtents(operation.dst_extents()) * block_size_));
469
470 constexpr size_t kMaxCacheSize = 5 * 1024 * 1024; // Total 5MB cache.
471 TEST_AND_RETURN_FALSE(
472 puffin::PuffPatch(std::move(src_stream),
473 std::move(dst_stream),
474 reinterpret_cast<const uint8_t*>(data),
475 count,
476 kMaxCacheSize));
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400477 return true;
Kelvin Zhang50bac652020-09-28 15:51:41 -0400478}
479
480FileDescriptorPtr PartitionWriter::ChooseSourceFD(
481 const InstallOperation& operation, ErrorCode* error) {
482 if (source_fd_ == nullptr) {
483 LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
484 return nullptr;
485 }
486
487 if (!operation.has_src_sha256_hash()) {
488 // When the operation doesn't include a source hash, we attempt the error
489 // corrected device first since we can't verify the block in the raw device
490 // at this point, but we first need to make sure all extents are readable
491 // since the error corrected device can be shorter or not available.
492 if (OpenCurrentECCPartition() &&
493 fd_utils::ReadAndHashExtents(
494 source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
495 return source_ecc_fd_;
496 }
497 return source_fd_;
498 }
499
500 brillo::Blob source_hash;
501 brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
502 operation.src_sha256_hash().end());
503 if (fd_utils::ReadAndHashExtents(
504 source_fd_, operation.src_extents(), block_size_, &source_hash) &&
505 source_hash == expected_source_hash) {
506 return source_fd_;
507 }
508 // We fall back to use the error corrected device if the hash of the raw
509 // device doesn't match or there was an error reading the source partition.
510 if (!OpenCurrentECCPartition()) {
511 // The following function call will return false since the source hash
512 // mismatches, but we still want to call it so it prints the appropriate
513 // log message.
514 ValidateSourceHash(source_hash, operation, source_fd_, error);
515 return nullptr;
516 }
517 LOG(WARNING) << "Source hash from RAW device mismatched: found "
518 << base::HexEncode(source_hash.data(), source_hash.size())
519 << ", expected "
520 << base::HexEncode(expected_source_hash.data(),
521 expected_source_hash.size());
522
523 if (fd_utils::ReadAndHashExtents(
524 source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
525 ValidateSourceHash(source_hash, operation, source_ecc_fd_, error)) {
526 // At this point reading from the error corrected device worked, but
527 // reading from the raw device failed, so this is considered a recovered
528 // failure.
529 source_ecc_recovered_failures_++;
530 return source_ecc_fd_;
531 }
532 return nullptr;
533}
534
535bool PartitionWriter::OpenCurrentECCPartition() {
536 // No support for ECC for full payloads.
537 // Full payload should not have any opeartion that requires ECC partitions.
538 if (source_ecc_fd_)
539 return true;
540
541 if (source_ecc_open_failure_)
542 return false;
543
544#if USE_FEC
545 const PartitionUpdate& partition = partition_update_;
546 const InstallPlan::Partition& install_part = install_part_;
547 std::string path = install_part.source_path;
548 FileDescriptorPtr fd(new FecFileDescriptor());
549 if (!fd->Open(path.c_str(), O_RDONLY, 0)) {
550 PLOG(ERROR) << "Unable to open ECC source partition "
551 << partition.partition_name() << ", file " << path;
552 source_ecc_open_failure_ = true;
553 return false;
554 }
555 source_ecc_fd_ = fd;
556#else
557 // No support for ECC compiled.
558 source_ecc_open_failure_ = true;
559#endif // USE_FEC
560
561 return !source_ecc_open_failure_;
562}
563
564int PartitionWriter::Close() {
565 int err = 0;
566 if (source_fd_ && !source_fd_->Close()) {
567 err = errno;
568 PLOG(ERROR) << "Error closing source partition";
569 if (!err)
570 err = 1;
571 }
572 source_fd_.reset();
573 source_path_.clear();
574
575 if (target_fd_ && !target_fd_->Close()) {
576 err = errno;
577 PLOG(ERROR) << "Error closing target partition";
578 if (!err)
579 err = 1;
580 }
581 target_fd_.reset();
582 target_path_.clear();
583
584 if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
585 err = errno;
586 PLOG(ERROR) << "Error closing ECC source partition";
587 if (!err)
588 err = 1;
589 }
590 source_ecc_fd_.reset();
591 source_ecc_open_failure_ = false;
592 return -err;
593}
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400594
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400595void PartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
596 target_fd_->Flush();
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400597}
598
Kelvin Zhang52cb1d72020-10-27 13:44:25 -0400599std::unique_ptr<ExtentWriter> PartitionWriter::CreateBaseExtentWriter() {
Kelvin Zhang4d22ca22021-02-09 14:06:25 -0500600 return std::make_unique<DirectExtentWriter>(target_fd_);
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400601}
602
Kelvin Zhang50bac652020-09-28 15:51:41 -0400603} // namespace chromeos_update_engine