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 <string>
20 #include <utility>
21 #include <vector>
22
23 #include <android-base/unique_fd.h>
24 #include <libsnapshot/cow_writer.h>
25
26 #include "update_engine/common/cow_operation_convert.h"
27 #include "update_engine/common/utils.h"
28 #include "update_engine/update_metadata.pb.h"
29
30 namespace chromeos_update_engine {
31 using android::snapshot::CowWriter;
32
33 namespace {
PerformReplaceOp(const InstallOperation & op,CowWriter * writer,FileDescriptorPtr target_fd,size_t block_size)34 bool PerformReplaceOp(const InstallOperation& op,
35 CowWriter* writer,
36 FileDescriptorPtr target_fd,
37 size_t block_size) {
38 std::vector<unsigned char> buffer;
39 for (const auto& extent : op.dst_extents()) {
40 buffer.resize(extent.num_blocks() * block_size);
41 // No need to read from payload.bin then decompress, just read from target
42 // directly.
43 ssize_t bytes_read = 0;
44 auto success = utils::ReadAll(target_fd,
45 buffer.data(),
46 buffer.size(),
47 extent.start_block() * block_size,
48 &bytes_read);
49 TEST_AND_RETURN_FALSE(success);
50 CHECK_EQ(static_cast<size_t>(bytes_read), buffer.size());
51 TEST_AND_RETURN_FALSE(writer->AddRawBlocks(
52 extent.start_block(), buffer.data(), buffer.size()));
53 }
54 return true;
55 }
56
PerformZeroOp(const InstallOperation & op,CowWriter * writer,size_t block_size)57 bool PerformZeroOp(const InstallOperation& op,
58 CowWriter* writer,
59 size_t block_size) {
60 for (const auto& extent : op.dst_extents()) {
61 TEST_AND_RETURN_FALSE(
62 writer->AddZeroBlocks(extent.start_block(), extent.num_blocks()));
63 }
64 return true;
65 }
66
WriteAllCowOps(size_t block_size,const std::vector<CowOperation> & converted,android::snapshot::ICowWriter * cow_writer,FileDescriptorPtr target_fd)67 bool WriteAllCowOps(size_t block_size,
68 const std::vector<CowOperation>& converted,
69 android::snapshot::ICowWriter* cow_writer,
70 FileDescriptorPtr target_fd) {
71 std::vector<uint8_t> buffer(block_size);
72
73 for (const auto& cow_op : converted) {
74 switch (cow_op.op) {
75 case CowOperation::CowCopy:
76 if (cow_op.src_block == cow_op.dst_block) {
77 continue;
78 }
79 TEST_AND_RETURN_FALSE(
80 cow_writer->AddCopy(cow_op.dst_block, cow_op.src_block));
81 break;
82 case CowOperation::CowReplace:
83 ssize_t bytes_read = 0;
84 TEST_AND_RETURN_FALSE(chromeos_update_engine::utils::ReadAll(
85 target_fd,
86 buffer.data(),
87 block_size,
88 cow_op.dst_block * block_size,
89 &bytes_read));
90 if (bytes_read <= 0 || static_cast<size_t>(bytes_read) != block_size) {
91 LOG(ERROR) << "source_fd->Read failed: " << bytes_read;
92 return false;
93 }
94 TEST_AND_RETURN_FALSE(cow_writer->AddRawBlocks(
95 cow_op.dst_block, buffer.data(), block_size));
96 break;
97 }
98 }
99
100 return true;
101 }
102 } // namespace
103
EstimateCowSize(FileDescriptorPtr target_fd,const google::protobuf::RepeatedPtrField<InstallOperation> & operations,const google::protobuf::RepeatedPtrField<CowMergeOperation> & merge_operations,size_t block_size,std::string compression)104 size_t EstimateCowSize(
105 FileDescriptorPtr target_fd,
106 const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
107 const google::protobuf::RepeatedPtrField<CowMergeOperation>&
108 merge_operations,
109 size_t block_size,
110 std::string compression) {
111 android::snapshot::CowWriter cow_writer{
112 {.block_size = static_cast<uint32_t>(block_size),
113 .compression = std::move(compression)}};
114 // CowWriter treats -1 as special value, will discard all the data but still
115 // reports Cow size. Good for estimation purposes
116 cow_writer.Initialize(android::base::borrowed_fd{-1});
117 CHECK(CowDryRun(
118 target_fd, operations, merge_operations, block_size, &cow_writer));
119 CHECK(cow_writer.Finalize());
120 return cow_writer.GetCowSize();
121 }
122
CowDryRun(FileDescriptorPtr target_fd,const google::protobuf::RepeatedPtrField<InstallOperation> & operations,const google::protobuf::RepeatedPtrField<CowMergeOperation> & merge_operations,size_t block_size,android::snapshot::CowWriter * cow_writer)123 bool CowDryRun(
124 FileDescriptorPtr target_fd,
125 const google::protobuf::RepeatedPtrField<InstallOperation>& operations,
126 const google::protobuf::RepeatedPtrField<CowMergeOperation>&
127 merge_operations,
128 size_t block_size,
129 android::snapshot::CowWriter* cow_writer) {
130 const auto converted = ConvertToCowOperations(operations, merge_operations);
131 WriteAllCowOps(block_size, converted, cow_writer, target_fd);
132 cow_writer->AddLabel(0);
133 for (const auto& op : operations) {
134 switch (op.type()) {
135 case InstallOperation::REPLACE:
136 case InstallOperation::REPLACE_BZ:
137 case InstallOperation::REPLACE_XZ:
138 TEST_AND_RETURN_FALSE(
139 PerformReplaceOp(op, cow_writer, target_fd, block_size));
140 break;
141 case InstallOperation::ZERO:
142 case InstallOperation::DISCARD:
143 TEST_AND_RETURN_FALSE(PerformZeroOp(op, cow_writer, block_size));
144 break;
145 case InstallOperation::SOURCE_COPY:
146 case InstallOperation::MOVE:
147 // Already handeled by WriteAllCowOps,
148 break;
149 case InstallOperation::SOURCE_BSDIFF:
150 case InstallOperation::BROTLI_BSDIFF:
151 case InstallOperation::PUFFDIFF:
152 case InstallOperation::BSDIFF:
153 // We might do something special by adding CowBsdiff to CowWriter.
154 // For now proceed the same way as normal REPLACE operation.
155 TEST_AND_RETURN_FALSE(
156 PerformReplaceOp(op, cow_writer, target_fd, block_size));
157 break;
158 }
159 // Arbitrary label number, we won't be resuming use these labels here.
160 // They are emitted just to keep size estimates accurate. As update_engine
161 // emits 1 label for every op.
162 cow_writer->AddLabel(2);
163 }
164 // TODO(zhangkelvin) Take FEC extents into account once VABC stabilizes
165 return true;
166 }
167 } // namespace chromeos_update_engine
168