• 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 
17 #include "update_engine/payload_generator/cow_size_estimator.h"
18 
19 #include <algorithm>
20 #include <functional>
21 #include <string>
22 #include <utility>
23 #include <vector>
24 
25 #include <android-base/unique_fd.h>
26 #include <libsnapshot/cow_writer.h>
27 
28 #include "update_engine/common/cow_operation_convert.h"
29 #include "update_engine/common/utils.h"
30 #include "update_engine/payload_consumer/vabc_partition_writer.h"
31 #include "update_engine/payload_generator/extent_ranges.h"
32 #include "update_engine/payload_generator/extent_utils.h"
33 #include "update_engine/update_metadata.pb.h"
34 
35 namespace chromeos_update_engine {
36 using android::snapshot::CowWriter;
37 
CowDryRun(FileDescriptorPtr source_fd,FileDescriptorPtr target_fd,const google::protobuf::RepeatedPtrField<InstallOperation> & operations,const google::protobuf::RepeatedPtrField<CowMergeOperation> & merge_operations,const size_t block_size,android::snapshot::CowWriter * cow_writer,const size_t partition_size,const bool xor_enabled)38 bool CowDryRun(
39     FileDescriptorPtr source_fd,
40     FileDescriptorPtr target_fd,
41     const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
42     const google::protobuf::RepeatedPtrField<CowMergeOperation>&
43         merge_operations,
44     const size_t block_size,
45     android::snapshot::CowWriter* cow_writer,
46     const size_t partition_size,
47     const bool xor_enabled) {
48   CHECK_NE(target_fd, nullptr);
49   CHECK(target_fd->IsOpen());
50   VABCPartitionWriter::WriteMergeSequence(merge_operations, cow_writer);
51   ExtentRanges visited;
52   for (const auto& op : merge_operations) {
53     if (op.type() == CowMergeOperation::COW_COPY) {
54       visited.AddExtent(op.dst_extent());
55       cow_writer->AddCopy(op.dst_extent().start_block(),
56                           op.src_extent().start_block(),
57                           op.dst_extent().num_blocks());
58     } else if (op.type() == CowMergeOperation::COW_XOR && xor_enabled) {
59       CHECK_NE(source_fd, nullptr) << "Source fd is required to enable XOR ops";
60       CHECK(source_fd->IsOpen());
61       visited.AddExtent(op.dst_extent());
62       // dst block count is used, because
63       // src block count is probably(if src_offset > 0) 1 block
64       // larger than dst extent. Using it might lead to intreseting out of bound
65       // disk reads.
66       std::vector<unsigned char> old_data(op.dst_extent().num_blocks() *
67                                           block_size);
68       ssize_t bytes_read = 0;
69       if (!utils::PReadAll(
70               source_fd,
71               old_data.data(),
72               old_data.size(),
73               op.src_extent().start_block() * block_size + op.src_offset(),
74               &bytes_read)) {
75         PLOG(ERROR) << "Failed to read source data at " << op.src_extent();
76         return false;
77       }
78       std::vector<unsigned char> new_data(op.dst_extent().num_blocks() *
79                                           block_size);
80       if (!utils::PReadAll(target_fd,
81                            new_data.data(),
82                            new_data.size(),
83                            op.dst_extent().start_block() * block_size,
84                            &bytes_read)) {
85         PLOG(ERROR) << "Failed to read target data at " << op.dst_extent();
86         return false;
87       }
88       CHECK_GT(old_data.size(), 0UL);
89       CHECK_GT(new_data.size(), 0UL);
90       std::transform(new_data.begin(),
91                      new_data.end(),
92                      old_data.begin(),
93                      new_data.begin(),
94                      std::bit_xor<unsigned char>{});
95       CHECK(cow_writer->AddXorBlocks(op.dst_extent().start_block(),
96                                      new_data.data(),
97                                      new_data.size(),
98                                      op.src_extent().start_block(),
99                                      op.src_offset()));
100     }
101     // The value of label doesn't really matter, we just want to write some
102     // labels to simulate bahvior of update_engine. As update_engine writes
103     // labels every once a while when installing OTA, it's important that we do
104     // the same to get accurate size estimation.
105     cow_writer->AddLabel(0);
106   }
107   for (const auto& op : operations) {
108     cow_writer->AddLabel(0);
109     if (op.type() == InstallOperation::ZERO) {
110       for (const auto& ext : op.dst_extents()) {
111         visited.AddExtent(ext);
112         cow_writer->AddZeroBlocks(ext.start_block(), ext.num_blocks());
113       }
114     }
115   }
116   cow_writer->AddLabel(0);
117   const size_t last_block = partition_size / block_size;
118   const auto unvisited_extents =
119       FilterExtentRanges({ExtentForRange(0, last_block)}, visited);
120   for (const auto& ext : unvisited_extents) {
121     std::vector<unsigned char> data(ext.num_blocks() * block_size);
122     ssize_t bytes_read = 0;
123     if (!utils::PReadAll(target_fd,
124                          data.data(),
125                          data.size(),
126                          ext.start_block() * block_size,
127                          &bytes_read)) {
128       PLOG(ERROR) << "Failed to read new block data at " << ext;
129       return false;
130     }
131     cow_writer->AddRawBlocks(ext.start_block(), data.data(), data.size());
132     cow_writer->AddLabel(0);
133   }
134 
135   return cow_writer->Finalize();
136 }
137 
EstimateCowSize(FileDescriptorPtr source_fd,FileDescriptorPtr target_fd,const google::protobuf::RepeatedPtrField<InstallOperation> & operations,const google::protobuf::RepeatedPtrField<CowMergeOperation> & merge_operations,const size_t block_size,std::string compression,const size_t partition_size,const bool xor_enabled)138 size_t EstimateCowSize(
139     FileDescriptorPtr source_fd,
140     FileDescriptorPtr target_fd,
141     const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
142     const google::protobuf::RepeatedPtrField<CowMergeOperation>&
143         merge_operations,
144     const size_t block_size,
145     std::string compression,
146     const size_t partition_size,
147     const bool xor_enabled) {
148   android::snapshot::CowWriter cow_writer{
149       {.block_size = static_cast<uint32_t>(block_size),
150        .compression = std::move(compression)}};
151   // CowWriter treats -1 as special value, will discard all the data but still
152   // reports Cow size. Good for estimation purposes
153   cow_writer.Initialize(android::base::borrowed_fd{-1});
154   CHECK(CowDryRun(source_fd,
155                   target_fd,
156                   operations,
157                   merge_operations,
158                   block_size,
159                   &cow_writer,
160                   partition_size,
161                   xor_enabled));
162   return cow_writer.GetCowSize();
163 }
164 
165 }  // namespace chromeos_update_engine
166