blob: 131f2fb60482c373470a7bb8c71d72123ffe37dc [file] [log] [blame]
Kelvin Zhangab3ce602021-02-24 14:46:40 -05001//
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// limi
15
16#include "update_engine/payload_consumer/verified_source_fd.h"
17
18#include <fcntl.h>
19#include <sys/stat.h>
20
21#include <memory>
Kelvin Zhangab3ce602021-02-24 14:46:40 -050022#include <vector>
23
24#include <base/strings/string_number_conversions.h>
Kelvin Zhangb9a9aa22024-10-15 10:38:35 -070025#include <android-base/stringprintf.h>
Kelvin Zhangab3ce602021-02-24 14:46:40 -050026
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070027#include "update_engine/common/error_code.h"
28#include "update_engine/common/hash_calculator.h"
Kelvin Zhangab3ce602021-02-24 14:46:40 -050029#include "update_engine/common/utils.h"
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070030#include "update_engine/payload_consumer/extent_writer.h"
31#include "update_engine/payload_consumer/file_descriptor.h"
Kelvin Zhangab3ce602021-02-24 14:46:40 -050032#include "update_engine/payload_consumer/file_descriptor_utils.h"
Kelvin Zhangab3ce602021-02-24 14:46:40 -050033#include "update_engine/payload_consumer/partition_writer.h"
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070034#include "update_engine/update_metadata.pb.h"
35#if USE_FEC
36#include "update_engine/payload_consumer/fec_file_descriptor.h"
37#endif
Kelvin Zhangab3ce602021-02-24 14:46:40 -050038
39namespace chromeos_update_engine {
40using std::string;
41
42bool VerifiedSourceFd::OpenCurrentECCPartition() {
43 // No support for ECC for full payloads.
44 // Full payload should not have any opeartion that requires ECC partitions.
45 if (source_ecc_fd_)
46 return true;
47
48 if (source_ecc_open_failure_)
49 return false;
50
51#if USE_FEC
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070052 auto fd = std::make_shared<FecFileDescriptor>();
Kelvin Zhangab3ce602021-02-24 14:46:40 -050053 if (!fd->Open(source_path_.c_str(), O_RDONLY, 0)) {
54 PLOG(ERROR) << "Unable to open ECC source partition " << source_path_;
55 source_ecc_open_failure_ = true;
56 return false;
57 }
58 source_ecc_fd_ = fd;
59#else
60 // No support for ECC compiled.
61 source_ecc_open_failure_ = true;
62#endif // USE_FEC
63
64 return !source_ecc_open_failure_;
65}
66
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070067bool VerifiedSourceFd::WriteBackCorrectedSourceBlocks(
68 const std::vector<unsigned char>& source_data,
69 const google::protobuf::RepeatedPtrField<Extent>& extents) {
Kelvin Zhangcf299152023-06-16 10:58:40 -070070 utils::SetBlockDeviceReadOnly(source_path_, false);
71 DEFER {
72 utils::SetBlockDeviceReadOnly(source_path_, true);
73 };
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070074 auto fd = std::make_shared<EintrSafeFileDescriptor>();
75 TEST_AND_RETURN_FALSE_ERRNO(fd->Open(source_path_.c_str(), O_RDWR));
76 DirectExtentWriter writer(fd);
77 TEST_AND_RETURN_FALSE(writer.Init(extents, block_size_));
Kelvin Zhangcf299152023-06-16 10:58:40 -070078 TEST_AND_RETURN_FALSE(writer.Write(source_data.data(), source_data.size()));
79 return true;
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070080}
81
Kelvin Zhangab3ce602021-02-24 14:46:40 -050082FileDescriptorPtr VerifiedSourceFd::ChooseSourceFD(
83 const InstallOperation& operation, ErrorCode* error) {
84 if (source_fd_ == nullptr) {
85 LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
86 return nullptr;
87 }
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070088 if (error) {
89 *error = ErrorCode::kSuccess;
90 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -050091 if (!operation.has_src_sha256_hash()) {
Kelvin Zhang212b0532024-11-15 10:56:06 -080092 if (operation.type() == InstallOperation::SOURCE_COPY) {
93 // delta_generator always adds SHA256 hash for source data. If hash is
94 // missing, the only possibility is we are doing a partial update, and
95 // currently processing a partition that's not in the payload. Data on
96 // this partition would be copied to the new slot as is. So, if the
97 // current partition boots fine(either no corruption, or with FEC), the
98 // new partition would boot fine as well. Hence, just return |source_fd_|
99 // to save time.
100 return source_fd_;
101 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500102 // When the operation doesn't include a source hash, we attempt the error
103 // corrected device first since we can't verify the block in the raw device
104 // at this point, but we first need to make sure all extents are readable
105 // since the error corrected device can be shorter or not available.
106 if (OpenCurrentECCPartition() &&
107 fd_utils::ReadAndHashExtents(
108 source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -0700109 if (error) {
110 *error = ErrorCode::kDownloadOperationHashMissingError;
111 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500112 return source_ecc_fd_;
113 }
114 return source_fd_;
115 }
116
117 brillo::Blob source_hash;
118 brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
119 operation.src_sha256_hash().end());
Kelvin Zhang8b07db52024-07-24 09:13:25 -0700120 if (!fd_utils::ReadAndHashExtents(
121 source_fd_, operation.src_extents(), block_size_, &source_hash)) {
122 LOG(ERROR) << "Failed to compute hash for operation " << operation.type()
123 << " data offset: " << operation.data_offset();
124 if (error) {
125 *error = ErrorCode::kDownloadOperationHashVerificationError;
126 }
127 return nullptr;
128 }
129 if (source_hash == expected_source_hash) {
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500130 return source_fd_;
131 }
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -0700132 if (error) {
133 *error = ErrorCode::kDownloadOperationHashMismatch;
134 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500135 // We fall back to use the error corrected device if the hash of the raw
136 // device doesn't match or there was an error reading the source partition.
137 if (!OpenCurrentECCPartition()) {
138 // The following function call will return false since the source hash
139 // mismatches, but we still want to call it so it prints the appropriate
140 // log message.
141 PartitionWriter::ValidateSourceHash(
142 source_hash, operation, source_fd_, error);
143 return nullptr;
144 }
145 LOG(WARNING) << "Source hash from RAW device mismatched: found "
146 << base::HexEncode(source_hash.data(), source_hash.size())
147 << ", expected "
148 << base::HexEncode(expected_source_hash.data(),
149 expected_source_hash.size());
150
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -0700151 std::vector<unsigned char> source_data;
152 if (!utils::ReadExtents(
153 source_ecc_fd_, operation.src_extents(), &source_data, block_size_)) {
154 return nullptr;
155 }
156 if (!HashCalculator::RawHashOfData(source_data, &source_hash)) {
157 return nullptr;
158 }
159 if (PartitionWriter::ValidateSourceHash(
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500160 source_hash, operation, source_ecc_fd_, error)) {
161 source_ecc_recovered_failures_++;
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -0700162 if (WriteBackCorrectedSourceBlocks(source_data, operation.src_extents())) {
163 if (error) {
164 *error = ErrorCode::kSuccess;
165 }
166 return source_fd_;
167 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500168 return source_ecc_fd_;
169 }
170 return nullptr;
171}
172
173bool VerifiedSourceFd::Open() {
174 source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
175 if (source_fd_ == nullptr)
176 return false;
Kelvin Zhang8fffea92023-07-25 20:34:26 -0700177 if (!source_fd_->Open(source_path_.c_str(), O_RDONLY)) {
178 PLOG(ERROR) << "Failed to open " << source_path_;
179 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500180 return true;
181}
182
183} // namespace chromeos_update_engine