• 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 <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