• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2021 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 <algorithm>
18 #include <optional>
19 #include <vector>
20 
21 #include "update_engine/common/utils.h"
22 #include "update_engine/payload_consumer/xor_extent_writer.h"
23 #include "update_engine/payload_generator/extent_utils.h"
24 
25 namespace chromeos_update_engine {
26 
27 // Returns true on success.
WriteExtent(const void * bytes,const Extent & extent,const size_t size)28 bool XORExtentWriter::WriteExtent(const void* bytes,
29                                   const Extent& extent,
30                                   const size_t size) {
31   brillo::Blob xor_block_data;
32   const auto xor_extents = xor_map_.GetIntersectingExtents(extent);
33   for (const auto& xor_ext : xor_extents) {
34     const auto merge_op_opt = xor_map_.Get(xor_ext);
35     if (!merge_op_opt.has_value()) {
36       // If a file in the target build contains duplicate blocks, e.g.
37       // [120503-120514], [120503-120503], we can end up here. If that's the
38       // case then there's no bug, just some annoying edge cases.
39       LOG(ERROR)
40           << xor_ext
41           << " isn't in XOR map but it's returned by GetIntersectingExtents(), "
42              "this is either a bug inside GetIntersectingExtents, or some "
43              "duplicate blocks are present in target build. OTA extent: "
44           << extent;
45       return false;
46     }
47 
48     const auto merge_op = merge_op_opt.value();
49     TEST_AND_RETURN_FALSE(merge_op->has_src_extent());
50     TEST_AND_RETURN_FALSE(merge_op->has_dst_extent());
51     if (!ExtentContains(extent, xor_ext)) {
52       LOG(ERROR) << "CowXor merge op extent should be completely inside "
53                     "InstallOp's extent. merge op extent: "
54                  << xor_ext << " InstallOp extent: " << extent;
55       return false;
56     }
57     if (!ExtentContains(merge_op->dst_extent(), xor_ext)) {
58       LOG(ERROR) << "CowXor op extent should be completely inside "
59                     "xor_map's extent. merge op extent: "
60                  << xor_ext << " xor_map extent: " << merge_op->dst_extent();
61       return false;
62     }
63     const auto src_offset = merge_op->src_offset();
64     const auto src_block = merge_op->src_extent().start_block() +
65                            xor_ext.start_block() -
66                            merge_op->dst_extent().start_block();
67     const auto i = xor_ext.start_block() - extent.start_block();
68     const auto dst_block_data =
69         static_cast<const unsigned char*>(bytes) + i * BlockSize();
70     const auto is_out_of_bound_read =
71         (src_block + xor_ext.num_blocks()) * BlockSize() + src_offset >
72             partition_size_ &&
73         partition_size_ != 0;
74     if (is_out_of_bound_read) {
75       LOG(INFO) << "Getting partial read for last block, converting "
76                    "XOR operation to a regular replace "
77                 << xor_ext;
78       TEST_AND_RETURN_FALSE(
79           cow_writer_->AddRawBlocks(xor_ext.start_block(),
80                                     dst_block_data,
81                                     xor_ext.num_blocks() * BlockSize()));
82       continue;
83     }
84     xor_block_data.resize(BlockSize() * xor_ext.num_blocks());
85     ssize_t bytes_read = 0;
86     TEST_AND_RETURN_FALSE_ERRNO(
87         utils::PReadAll(source_fd_,
88                         xor_block_data.data(),
89                         xor_block_data.size(),
90                         src_offset + src_block * BlockSize(),
91                         &bytes_read));
92     if (bytes_read != static_cast<ssize_t>(xor_block_data.size())) {
93       LOG(ERROR) << "bytes_read: " << bytes_read << ", expected to read "
94                  << xor_block_data.size() << " at block " << src_block
95                  << " offset " << src_offset;
96       return false;
97     }
98 
99     std::transform(xor_block_data.cbegin(),
100                    xor_block_data.cbegin() + xor_block_data.size(),
101                    dst_block_data,
102                    xor_block_data.begin(),
103                    std::bit_xor<unsigned char>{});
104     TEST_AND_RETURN_FALSE(cow_writer_->AddXorBlocks(xor_ext.start_block(),
105                                                     xor_block_data.data(),
106                                                     xor_block_data.size(),
107                                                     src_block,
108                                                     src_offset));
109   }
110   const auto replace_extents = xor_map_.GetNonIntersectingExtents(extent);
111   return WriteReplaceExtents(replace_extents, extent, bytes, size);
112 }
113 
WriteReplaceExtents(const std::vector<Extent> & replace_extents,const Extent & extent,const void * bytes,size_t size)114 bool XORExtentWriter::WriteReplaceExtents(
115     const std::vector<Extent>& replace_extents,
116     const Extent& extent,
117     const void* bytes,
118     size_t size) {
119   const uint64_t new_block_start = extent.start_block();
120   for (const auto& ext : replace_extents) {
121     if (ext.start_block() + ext.num_blocks() >
122         extent.start_block() + extent.num_blocks()) {
123       LOG(ERROR) << "CowReplace merge op extent should be completely inside "
124                     "InstallOp's extent. merge op extent: "
125                  << ext << " InstallOp extent: " << extent;
126       return false;
127     }
128     const auto i = ext.start_block() - new_block_start;
129     const auto dst_block_data =
130         static_cast<const unsigned char*>(bytes) + i * BlockSize();
131     TEST_AND_RETURN_FALSE(cow_writer_->AddRawBlocks(
132         ext.start_block(), dst_block_data, ext.num_blocks() * BlockSize()));
133   }
134   return true;
135 }
136 
137 }  // namespace chromeos_update_engine
138