• 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_ranges.h"
24 #include "update_engine/payload_generator/extent_utils.h"
25 #include "update_engine/update_metadata.pb.h"
26 
27 namespace chromeos_update_engine {
WriteXorCowOp(const uint8_t * bytes,const size_t size,const Extent & xor_ext,const size_t src_offset)28 bool XORExtentWriter::WriteXorCowOp(const uint8_t* bytes,
29                                     const size_t size,
30                                     const Extent& xor_ext,
31                                     const size_t src_offset) {
32   xor_block_data.resize(BlockSize() * xor_ext.num_blocks());
33   const auto src_block = src_offset / BlockSize();
34   ssize_t bytes_read = 0;
35   TEST_AND_RETURN_FALSE_ERRNO(utils::PReadAll(source_fd_,
36                                               xor_block_data.data(),
37                                               xor_block_data.size(),
38                                               src_offset,
39                                               &bytes_read));
40   if (bytes_read != static_cast<ssize_t>(xor_block_data.size())) {
41     LOG(ERROR) << "bytes_read: " << bytes_read << ", expected to read "
42                << xor_block_data.size() << " at block " << src_block
43                << " offset " << src_offset % BlockSize();
44     return false;
45   }
46 
47   std::transform(xor_block_data.cbegin(),
48                  xor_block_data.cbegin() + xor_block_data.size(),
49                  bytes,
50                  xor_block_data.begin(),
51                  std::bit_xor<unsigned char>{});
52   TEST_AND_RETURN_FALSE(cow_writer_->AddXorBlocks(xor_ext.start_block(),
53                                                   xor_block_data.data(),
54                                                   xor_block_data.size(),
55                                                   src_block,
56                                                   src_offset % BlockSize()));
57   return true;
58 }
59 
WriteXorExtent(const uint8_t * bytes,const size_t size,const Extent & xor_ext,const CowMergeOperation * merge_op)60 bool XORExtentWriter::WriteXorExtent(const uint8_t* bytes,
61                                      const size_t size,
62                                      const Extent& xor_ext,
63                                      const CowMergeOperation* merge_op) {
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 read_end_offset =
68       (src_block + xor_ext.num_blocks()) * BlockSize() + merge_op->src_offset();
69   const auto is_out_of_bound_read =
70       read_end_offset > partition_size_ && partition_size_ != 0;
71   const auto oob_bytes =
72       is_out_of_bound_read ? read_end_offset - partition_size_ : 0;
73   if (is_out_of_bound_read) {
74     if (oob_bytes >= BlockSize()) {
75       LOG(ERROR) << "XOR op overflowed source partition by more than "
76                  << BlockSize() << ", " << xor_ext << ", " << merge_op
77                  << ", out of bound bytes: " << oob_bytes
78                  << ", partition size: " << partition_size_;
79       return false;
80     }
81     if (oob_bytes > merge_op->src_offset()) {
82       LOG(ERROR) << "XOR op overflowed source offset, out of bound bytes: "
83                  << oob_bytes << ", source offset: " << merge_op->src_offset();
84     }
85     Extent non_oob_extent =
86         ExtentForRange(xor_ext.start_block(), xor_ext.num_blocks() - 1);
87     if (non_oob_extent.num_blocks() > 0) {
88       TEST_AND_RETURN_FALSE(
89           WriteXorCowOp(bytes,
90                         BlockSize() * non_oob_extent.num_blocks(),
91                         non_oob_extent,
92                         src_block * BlockSize() + merge_op->src_offset()));
93     }
94     const Extent last_block =
95         ExtentForRange(xor_ext.start_block() + xor_ext.num_blocks() - 1, 1);
96     TEST_AND_RETURN_FALSE(
97         WriteXorCowOp(bytes + (xor_ext.num_blocks() - 1) * BlockSize(),
98                       BlockSize(),
99                       last_block,
100                       (src_block + xor_ext.num_blocks() - 1) * BlockSize()));
101     return true;
102   }
103   TEST_AND_RETURN_FALSE(WriteXorCowOp(
104       bytes, size, xor_ext, src_block * BlockSize() + merge_op->src_offset()));
105   return true;
106 }
107 
108 // Returns true on success.
WriteExtent(const void * bytes,const Extent & extent,const size_t size)109 bool XORExtentWriter::WriteExtent(const void* bytes,
110                                   const Extent& extent,
111                                   const size_t size) {
112   const auto xor_extents = xor_map_.GetIntersectingExtents(extent);
113   for (const auto& xor_ext : xor_extents) {
114     const auto merge_op_opt = xor_map_.Get(xor_ext);
115     if (!merge_op_opt.has_value()) {
116       // If a file in the target build contains duplicate blocks, e.g.
117       // [120503-120514], [120503-120503], we can end up here. If that's the
118       // case then there's no bug, just some annoying edge cases.
119       LOG(ERROR)
120           << xor_ext
121           << " isn't in XOR map but it's returned by GetIntersectingExtents(), "
122              "this is either a bug inside GetIntersectingExtents, or some "
123              "duplicate blocks are present in target build. OTA extent: "
124           << extent;
125       return false;
126     }
127 
128     const auto merge_op = merge_op_opt.value();
129     TEST_AND_RETURN_FALSE(merge_op->has_src_extent());
130     TEST_AND_RETURN_FALSE(merge_op->has_dst_extent());
131     if (!ExtentContains(extent, xor_ext)) {
132       LOG(ERROR) << "CowXor merge op extent should be completely inside "
133                     "InstallOp's extent. merge op extent: "
134                  << xor_ext << " InstallOp extent: " << extent;
135       return false;
136     }
137     if (!ExtentContains(merge_op->dst_extent(), xor_ext)) {
138       LOG(ERROR) << "CowXor op extent should be completely inside "
139                     "xor_map's extent. merge op extent: "
140                  << xor_ext << " xor_map extent: " << merge_op->dst_extent();
141       return false;
142     }
143     const auto i = xor_ext.start_block() - extent.start_block();
144     const auto dst_block_data =
145         static_cast<const unsigned char*>(bytes) + i * BlockSize();
146     if (!WriteXorExtent(dst_block_data,
147                         xor_ext.num_blocks() * BlockSize(),
148                         xor_ext,
149                         merge_op)) {
150       LOG(ERROR) << "Failed to write XOR extent " << xor_ext;
151       return false;
152     }
153   }
154   const auto replace_extents = xor_map_.GetNonIntersectingExtents(extent);
155   return WriteReplaceExtents(replace_extents, extent, bytes, size);
156 }
157 
WriteReplaceExtents(const std::vector<Extent> & replace_extents,const Extent & extent,const void * bytes,size_t size)158 bool XORExtentWriter::WriteReplaceExtents(
159     const std::vector<Extent>& replace_extents,
160     const Extent& extent,
161     const void* bytes,
162     size_t size) {
163   const uint64_t new_block_start = extent.start_block();
164   for (const auto& ext : replace_extents) {
165     if (ext.start_block() + ext.num_blocks() >
166         extent.start_block() + extent.num_blocks()) {
167       LOG(ERROR) << "CowReplace merge op extent should be completely inside "
168                     "InstallOp's extent. merge op extent: "
169                  << ext << " InstallOp extent: " << extent;
170       return false;
171     }
172     const auto i = ext.start_block() - new_block_start;
173     const auto dst_block_data =
174         static_cast<const unsigned char*>(bytes) + i * BlockSize();
175     TEST_AND_RETURN_FALSE(cow_writer_->AddRawBlocks(
176         ext.start_block(), dst_block_data, ext.num_blocks() * BlockSize()));
177   }
178   return true;
179 }
180 
181 }  // namespace chromeos_update_engine
182