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 (merge_op->dst_extent() != xor_ext) {
52 LOG(ERROR) << "Each xor extent is expected to correspond to a complete "
53 "MergeOp, extent in value: "
54 << merge_op->dst_extent() << " extent in key: " << xor_ext;
55 return false;
56 }
57 if (xor_ext.start_block() + xor_ext.num_blocks() >
58 extent.start_block() + extent.num_blocks()) {
59 LOG(ERROR) << "CowXor merge op extent should be completely inside "
60 "InstallOp's extent. merge op extent: "
61 << xor_ext << " InstallOp extent: " << extent;
62 return false;
63 }
64 const auto src_offset = merge_op->src_offset();
65 const auto src_block = merge_op->src_extent().start_block();
66 xor_block_data.resize(BlockSize() * xor_ext.num_blocks());
67 ssize_t bytes_read = 0;
68 TEST_AND_RETURN_FALSE_ERRNO(
69 utils::PReadAll(source_fd_,
70 xor_block_data.data(),
71 xor_block_data.size(),
72 src_offset + src_block * BlockSize(),
73 &bytes_read));
74 if (bytes_read != static_cast<ssize_t>(xor_block_data.size())) {
75 LOG(ERROR) << "bytes_read: " << bytes_read;
76 return false;
77 }
78
79 const auto i = xor_ext.start_block() - extent.start_block();
80
81 const auto dst_block_data =
82 static_cast<const unsigned char*>(bytes) + i * BlockSize();
83 std::transform(xor_block_data.cbegin(),
84 xor_block_data.cbegin() + xor_block_data.size(),
85 dst_block_data,
86 xor_block_data.begin(),
87 std::bit_xor<unsigned char>{});
88 TEST_AND_RETURN_FALSE(cow_writer_->AddXorBlocks(xor_ext.start_block(),
89 xor_block_data.data(),
90 xor_block_data.size(),
91 src_block,
92 src_offset));
93 }
94 const auto replace_extents = xor_map_.GetNonIntersectingExtents(extent);
95 return WriteReplaceExtents(replace_extents, extent, bytes, size);
96 }
97
WriteReplaceExtents(const std::vector<Extent> & replace_extents,const Extent & extent,const void * bytes,size_t size)98 bool XORExtentWriter::WriteReplaceExtents(
99 const std::vector<Extent>& replace_extents,
100 const Extent& extent,
101 const void* bytes,
102 size_t size) {
103 const uint64_t new_block_start = extent.start_block();
104 for (const auto& ext : replace_extents) {
105 if (ext.start_block() + ext.num_blocks() >
106 extent.start_block() + extent.num_blocks()) {
107 LOG(ERROR) << "CowReplace merge op extent should be completely inside "
108 "InstallOp's extent. merge op extent: "
109 << ext << " InstallOp extent: " << extent;
110 return false;
111 }
112 const auto i = ext.start_block() - new_block_start;
113 const auto dst_block_data =
114 static_cast<const unsigned char*>(bytes) + i * BlockSize();
115 TEST_AND_RETURN_FALSE(cow_writer_->AddRawBlocks(
116 ext.start_block(), dst_block_data, ext.num_blocks() * BlockSize()));
117 }
118 return true;
119 }
120
121 } // namespace chromeos_update_engine
122