1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "partition_cow_creator.h"
16
17 #include <math.h>
18
19 #include <android-base/logging.h>
20 #include <android/snapshot/snapshot.pb.h>
21 #include <storage_literals/storage_literals.h>
22
23 #include "dm_snapshot_internals.h"
24 #include "utility.h"
25
26 using android::dm::kSectorSize;
27 using android::fs_mgr::Extent;
28 using android::fs_mgr::Interval;
29 using android::fs_mgr::kDefaultBlockSize;
30 using android::fs_mgr::Partition;
31 using chromeos_update_engine::InstallOperation;
32 template <typename T>
33 using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
34
35 namespace android {
36 namespace snapshot {
37
38 static constexpr uint64_t kBlockSize = 4096;
39
40 using namespace android::storage_literals;
41
42 // Intersect two linear extents. If no intersection, return an extent with length 0.
Intersect(Extent * target_extent,Extent * existing_extent)43 static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
44 // Convert target_extent and existing_extent to linear extents. Zero extents
45 // doesn't matter and doesn't result in any intersection.
46 auto existing_linear_extent = existing_extent->AsLinearExtent();
47 if (!existing_linear_extent) return nullptr;
48
49 auto target_linear_extent = target_extent->AsLinearExtent();
50 if (!target_linear_extent) return nullptr;
51
52 return Interval::Intersect(target_linear_extent->AsInterval(),
53 existing_linear_extent->AsInterval())
54 .AsExtent();
55 }
56
57 // Check that partition |p| contains |e| fully. Both of them should
58 // be from |target_metadata|.
59 // Returns true as long as |e| is a subrange of any extent of |p|.
HasExtent(Partition * p,Extent * e)60 bool PartitionCowCreator::HasExtent(Partition* p, Extent* e) {
61 for (auto& partition_extent : p->extents()) {
62 auto intersection = Intersect(partition_extent.get(), e);
63 if (intersection != nullptr && intersection->num_sectors() == e->num_sectors()) {
64 return true;
65 }
66 }
67 return false;
68 }
69
OptimizeSourceCopyOperation(const InstallOperation & operation,InstallOperation * optimized)70 bool OptimizeSourceCopyOperation(const InstallOperation& operation, InstallOperation* optimized) {
71 if (operation.type() != InstallOperation::SOURCE_COPY) {
72 return false;
73 }
74
75 optimized->Clear();
76 optimized->set_type(InstallOperation::SOURCE_COPY);
77
78 const auto& src_extents = operation.src_extents();
79 const auto& dst_extents = operation.dst_extents();
80
81 // If input is empty, skip by returning an empty result.
82 if (src_extents.empty() && dst_extents.empty()) {
83 return true;
84 }
85
86 auto s_it = src_extents.begin();
87 auto d_it = dst_extents.begin();
88 uint64_t s_offset = 0; // offset within *s_it
89 uint64_t d_offset = 0; // offset within *d_it
90 bool is_optimized = false;
91
92 while (s_it != src_extents.end() || d_it != dst_extents.end()) {
93 if (s_it == src_extents.end() || d_it == dst_extents.end()) {
94 LOG(ERROR) << "number of blocks do not equal in src_extents and dst_extents";
95 return false;
96 }
97 if (s_it->num_blocks() <= s_offset || d_it->num_blocks() <= d_offset) {
98 LOG(ERROR) << "Offset goes out of bounds.";
99 return false;
100 }
101
102 // Check the next |step| blocks, where |step| is the min of remaining blocks in the current
103 // source extent and current destination extent.
104 auto s_step = s_it->num_blocks() - s_offset;
105 auto d_step = d_it->num_blocks() - d_offset;
106 auto step = std::min(s_step, d_step);
107
108 bool moved = s_it->start_block() + s_offset != d_it->start_block() + d_offset;
109 if (moved) {
110 // If the next |step| blocks are not copied to the same location, add them to result.
111 AppendExtent(optimized->mutable_src_extents(), s_it->start_block() + s_offset, step);
112 AppendExtent(optimized->mutable_dst_extents(), d_it->start_block() + d_offset, step);
113 } else {
114 // The next |step| blocks are optimized out.
115 is_optimized = true;
116 }
117
118 // Advance offsets by |step|, and go to the next non-empty extent if the current extent is
119 // depleted.
120 s_offset += step;
121 d_offset += step;
122 while (s_it != src_extents.end() && s_offset >= s_it->num_blocks()) {
123 ++s_it;
124 s_offset = 0;
125 }
126 while (d_it != dst_extents.end() && d_offset >= d_it->num_blocks()) {
127 ++d_it;
128 d_offset = 0;
129 }
130 }
131 return is_optimized;
132 }
133
WriteExtent(DmSnapCowSizeCalculator * sc,const chromeos_update_engine::Extent & de,unsigned int sectors_per_block)134 bool WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
135 unsigned int sectors_per_block) {
136 const auto block_boundary = de.start_block() + de.num_blocks();
137 for (auto b = de.start_block(); b < block_boundary; ++b) {
138 for (unsigned int s = 0; s < sectors_per_block; ++s) {
139 // sector_id = b * sectors_per_block + s;
140 uint64_t block_start_sector_id;
141 if (__builtin_mul_overflow(b, sectors_per_block, &block_start_sector_id)) {
142 LOG(ERROR) << "Integer overflow when calculating sector id (" << b << " * "
143 << sectors_per_block << ")";
144 return false;
145 }
146 uint64_t sector_id;
147 if (__builtin_add_overflow(block_start_sector_id, s, §or_id)) {
148 LOG(ERROR) << "Integer overflow when calculating sector id ("
149 << block_start_sector_id << " + " << s << ")";
150 return false;
151 }
152 sc->WriteSector(sector_id);
153 }
154 }
155 return true;
156 }
157
GetCowSize()158 std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
159 if (using_snapuserd) {
160 if (update == nullptr || !update->has_estimate_cow_size()) {
161 LOG(ERROR) << "Update manifest does not include a COW size";
162 return std::nullopt;
163 }
164
165 // Add an extra 2MB of wiggle room for any minor differences in labels/metadata
166 // that might come up.
167 auto size = update->estimate_cow_size() + 2_MiB;
168
169 // Align to nearest block.
170 size += kBlockSize - 1;
171 size &= ~(kBlockSize - 1);
172 return size;
173 }
174
175 // WARNING: The origin partition should be READ-ONLY
176 const uint64_t logical_block_size = current_metadata->logical_block_size();
177 const unsigned int sectors_per_block = logical_block_size / kSectorSize;
178 DmSnapCowSizeCalculator sc(kSectorSize, kSnapshotChunkSize);
179
180 // Allocate space for extra extents (if any). These extents are those that can be
181 // used for error corrections or to store verity hash trees.
182 for (const auto& de : extra_extents) {
183 if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;
184 }
185
186 if (update == nullptr) return sc.cow_size_bytes();
187
188 for (const auto& iop : update->operations()) {
189 const InstallOperation* written_op = &iop;
190 InstallOperation buf;
191 // Do not allocate space for extents that are going to be skipped
192 // during OTA application.
193 if (iop.type() == InstallOperation::SOURCE_COPY && OptimizeSourceCopyOperation(iop, &buf)) {
194 written_op = &buf;
195 }
196
197 for (const auto& de : written_op->dst_extents()) {
198 if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;
199 }
200 }
201
202 return sc.cow_size_bytes();
203 }
204
Run()205 std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
206 CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
207 target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
208
209 const uint64_t logical_block_size = current_metadata->logical_block_size();
210 CHECK(logical_block_size != 0 && !(logical_block_size & (logical_block_size - 1)))
211 << "logical_block_size is not power of 2";
212
213 Return ret;
214 ret.snapshot_status.set_name(target_partition->name());
215 ret.snapshot_status.set_device_size(target_partition->size());
216 ret.snapshot_status.set_snapshot_size(target_partition->size());
217
218 if (update && update->has_estimate_cow_size()) {
219 ret.snapshot_status.set_estimated_cow_size(update->estimate_cow_size());
220 ret.snapshot_status.set_estimated_ops_buffer_size(update->estimate_op_count_max());
221 }
222
223 if (ret.snapshot_status.snapshot_size() == 0) {
224 LOG(INFO) << "Not creating snapshot for partition " << ret.snapshot_status.name();
225 ret.snapshot_status.set_cow_partition_size(0);
226 ret.snapshot_status.set_cow_file_size(0);
227 return ret;
228 }
229
230 // Being the COW partition virtual, its size doesn't affect the storage
231 // memory that will be occupied by the target.
232 // The actual storage space is affected by the COW file, whose size depends
233 // on the chunks that diverged between |current| and |target|.
234 // If the |target| partition is bigger than |current|, the data that is
235 // modified outside of |current| can be written directly to |current|.
236 // This because the data that will be written outside of |current| would
237 // not invalidate any useful information of |current|, thus:
238 // - if the snapshot is accepted for merge, this data would be already at
239 // the right place and should not be copied;
240 // - in the unfortunate case of the snapshot to be discarded, the regions
241 // modified by this data can be set as free regions and reused.
242 // Compute regions that are free in both current and target metadata. These are the regions
243 // we can use for COW partition.
244 auto target_free_regions = target_metadata->GetFreeRegions();
245 auto current_free_regions = current_metadata->GetFreeRegions();
246 auto free_regions = Interval::Intersect(target_free_regions, current_free_regions);
247 uint64_t free_region_length = 0;
248 for (const auto& interval : free_regions) {
249 free_region_length += interval.length();
250 }
251 free_region_length *= kSectorSize;
252
253 LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
254 auto cow_size = GetCowSize();
255 if (!cow_size) {
256 return {};
257 }
258
259 // Compute the COW partition size.
260 uint64_t cow_partition_size = std::min(cow_size.value(), free_region_length);
261 // Round it down to the nearest logical block. Logical partitions must be a multiple
262 // of logical blocks.
263 cow_partition_size &= ~(logical_block_size - 1);
264 ret.snapshot_status.set_cow_partition_size(cow_partition_size);
265 // Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
266 ret.cow_partition_usable_regions = std::move(free_regions);
267
268 auto cow_file_size = cow_size.value() - cow_partition_size;
269 // Round it up to the nearest sector.
270 cow_file_size += kSectorSize - 1;
271 cow_file_size &= ~(kSectorSize - 1);
272 ret.snapshot_status.set_cow_file_size(cow_file_size);
273
274 return ret;
275 }
276
277 } // namespace snapshot
278 } // namespace android
279