blob: 9e4d9b8a2d6dcdebb9106574c48ce4e3d97472c3 [file] [log] [blame]
Kelvin Zhang94f51cc2020-09-25 11:34:49 -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
17#include "update_engine/payload_consumer/vabc_partition_writer.h"
18
19#include <memory>
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040020#include <vector>
Kelvin Zhang94f51cc2020-09-25 11:34:49 -040021
22#include <libsnapshot/cow_writer.h>
23
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040024#include "update_engine/common/cow_operation_convert.h"
Kelvin Zhang94f51cc2020-09-25 11:34:49 -040025#include "update_engine/common/utils.h"
26#include "update_engine/payload_consumer/extent_writer.h"
27#include "update_engine/payload_consumer/install_plan.h"
28#include "update_engine/payload_consumer/partition_writer.h"
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040029#include "update_engine/payload_consumer/snapshot_extent_writer.h"
Kelvin Zhang94f51cc2020-09-25 11:34:49 -040030
31namespace chromeos_update_engine {
Kelvin Zhang24599af2020-10-27 13:44:25 -040032// Expected layout of COW file:
33// === Beginning of Cow Image ===
34// All Source Copy Operations
35// ========== Label 0 ==========
36// Operation 0 in PartitionUpdate
37// ========== Label 1 ==========
38// Operation 1 in PartitionUpdate
39// ========== label 2 ==========
40// Operation 2 in PartitionUpdate
41// ========== label 3 ==========
42// .
43// .
44// .
45
46// When resuming, pass |kPrefsUpdateStatePartitionNextOperation| as label to
47// |InitializeWithAppend|.
48// For example, suppose we finished writing SOURCE_COPY, and we finished writing
49// operation 2 completely. Update is suspended when we are half way through
50// operation 3.
51// |kPrefsUpdateStatePartitionNextOperation| would be 3, so we pass 3 as
52// label to |InitializeWithAppend|. The CowWriter will retain all data before
53// label 3, Which contains all operation 2's data, but none of operation 3's
54// data.
55
Kelvin Zhang94f51cc2020-09-25 11:34:49 -040056bool VABCPartitionWriter::Init(const InstallPlan* install_plan,
57 bool source_may_exist) {
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040058 TEST_AND_RETURN_FALSE(install_plan != nullptr);
Kelvin Zhang94f51cc2020-09-25 11:34:49 -040059 TEST_AND_RETURN_FALSE(PartitionWriter::Init(install_plan, source_may_exist));
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040060 cow_writer_ = dynamic_control_->OpenCowWriter(
61 install_part_.name, install_part_.source_path, install_plan->is_resume);
62 TEST_AND_RETURN_FALSE(cow_writer_ != nullptr);
Kelvin Zhang94f51cc2020-09-25 11:34:49 -040063
Kelvin Zhang24599af2020-10-27 13:44:25 -040064 // Emit a label before writing SOURCE_COPY. When resuming,
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040065 // use pref or CowWriter::GetLastLabel to determine if the SOURCE_COPY ops are
66 // written. No need to handle SOURCE_COPY operations when resuming.
67
68 // ===== Resume case handling code goes here ====
Kelvin Zhang24599af2020-10-27 13:44:25 -040069 if (install_plan->is_resume) {
70 int64_t next_op = 0;
71 if (!prefs_->GetInt64(kPrefsUpdateStatePartitionNextOperation, &next_op)) {
72 LOG(ERROR)
73 << "Resuming an update but can't fetch |next_op| from saved prefs.";
74 return false;
75 }
76 if (next_op < 0) {
77 TEST_AND_RETURN_FALSE(cow_writer_->Initialize());
78 } else {
79 TEST_AND_RETURN_FALSE(cow_writer_->InitializeAppend(next_op));
80 return true;
81 }
82 } else {
83 TEST_AND_RETURN_FALSE(cow_writer_->Initialize());
84 }
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040085
86 // ==============================================
Kelvin Zhang24599af2020-10-27 13:44:25 -040087 TEST_AND_RETURN_FALSE(
88 prefs_->SetInt64(kPrefsUpdateStatePartitionNextOperation, -1));
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040089
90 // TODO(zhangkelvin) Rewrite this in C++20 coroutine once that's available.
91 auto converted = ConvertToCowOperations(partition_update_.operations(),
92 partition_update_.merge_operations());
93 std::vector<uint8_t> buffer(block_size_);
Kelvin Zhang24599af2020-10-27 13:44:25 -040094
Kelvin Zhang9b10dba2020-09-25 17:09:11 -040095 for (const auto& cow_op : converted) {
96 switch (cow_op.op) {
97 case CowOperation::CowCopy:
98 TEST_AND_RETURN_FALSE(
99 cow_writer_->AddCopy(cow_op.dst_block, cow_op.src_block));
100 break;
101 case CowOperation::CowReplace:
102 ssize_t bytes_read = 0;
103 TEST_AND_RETURN_FALSE(utils::PReadAll(source_fd_,
104 buffer.data(),
105 block_size_,
106 cow_op.src_block * block_size_,
107 &bytes_read));
108 if (bytes_read <= 0 || static_cast<size_t>(bytes_read) != block_size_) {
109 LOG(ERROR) << "source_fd->Read failed: " << bytes_read;
110 return false;
111 }
112 TEST_AND_RETURN_FALSE(cow_writer_->AddRawBlocks(
113 cow_op.dst_block, buffer.data(), block_size_));
114 break;
115 }
116 }
Kelvin Zhang24599af2020-10-27 13:44:25 -0400117
118 // Emit label 0 to mark end of all SOURCE_COPY operations
119 cow_writer_->AddLabel(0);
120 TEST_AND_RETURN_FALSE(
121 prefs_->SetInt64(kPrefsUpdateStatePartitionNextOperation, 0));
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400122 return true;
123}
124
125std::unique_ptr<ExtentWriter> VABCPartitionWriter::CreateBaseExtentWriter() {
Kelvin Zhang9b10dba2020-09-25 17:09:11 -0400126 return std::make_unique<SnapshotExtentWriter>(cow_writer_.get());
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400127}
128
129[[nodiscard]] bool VABCPartitionWriter::PerformZeroOrDiscardOperation(
130 const InstallOperation& operation) {
Kelvin Zhang9b10dba2020-09-25 17:09:11 -0400131 for (const auto& extent : operation.dst_extents()) {
132 TEST_AND_RETURN_FALSE(
133 cow_writer_->AddZeroBlocks(extent.start_block(), extent.num_blocks()));
134 }
135 return true;
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400136}
137
138[[nodiscard]] bool VABCPartitionWriter::PerformSourceCopyOperation(
139 const InstallOperation& operation, ErrorCode* error) {
140 // TODO(zhangkelvin) Probably just ignore SOURCE_COPY? They should be taken
141 // care of during Init();
142 return true;
143}
144
Kelvin Zhang9b10dba2020-09-25 17:09:11 -0400145bool VABCPartitionWriter::Flush() {
Kelvin Zhang24599af2020-10-27 13:44:25 -0400146 // No need to call fsync/sync, as CowWriter flushes after a label is added
147 // added.
148 int64_t next_op = 0;
149 // |kPrefsUpdateStatePartitionNextOperation| will be maintained and set by
150 // CheckpointUpdateProgress()
151 TEST_AND_RETURN_FALSE(
152 prefs_->GetInt64(kPrefsUpdateStatePartitionNextOperation, &next_op));
153 // +1 because label 0 is reserved for SOURCE_COPY. See beginning of this
154 // file for explanation for cow format.
155 cow_writer_->AddLabel(next_op + 1);
Kelvin Zhang9b10dba2020-09-25 17:09:11 -0400156 return true;
157}
158
Kelvin Zhang24599af2020-10-27 13:44:25 -0400159void VABCPartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
160 prefs_->SetInt64(kPrefsUpdateStatePartitionNextOperation, next_op_index);
161}
162
Kelvin Zhang9b10dba2020-09-25 17:09:11 -0400163VABCPartitionWriter::~VABCPartitionWriter() {
Kelvin Zhang24599af2020-10-27 13:44:25 -0400164 // Reset |kPrefsUpdateStatePartitionNextOperation| once we finished a
165 // partition.
166 prefs_->SetInt64(kPrefsUpdateStatePartitionNextOperation, -1);
Kelvin Zhang9b10dba2020-09-25 17:09:11 -0400167 cow_writer_->Finalize();
168}
Kelvin Zhang94f51cc2020-09-25 11:34:49 -0400169
170} // namespace chromeos_update_engine