blob: addf53406b60f723737e2f9706af14e2085ce982 [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>
22#include <utility>
23#include <vector>
24
25#include <base/strings/string_number_conversions.h>
Kelvin Zhangb9a9aa22024-10-15 10:38:35 -070026#include <android-base/stringprintf.h>
Kelvin Zhangab3ce602021-02-24 14:46:40 -050027
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070028#include "update_engine/common/error_code.h"
29#include "update_engine/common/hash_calculator.h"
Kelvin Zhangab3ce602021-02-24 14:46:40 -050030#include "update_engine/common/utils.h"
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070031#include "update_engine/payload_consumer/extent_writer.h"
32#include "update_engine/payload_consumer/file_descriptor.h"
Kelvin Zhangab3ce602021-02-24 14:46:40 -050033#include "update_engine/payload_consumer/file_descriptor_utils.h"
Kelvin Zhangab3ce602021-02-24 14:46:40 -050034#include "update_engine/payload_consumer/partition_writer.h"
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070035#include "update_engine/update_metadata.pb.h"
36#if USE_FEC
37#include "update_engine/payload_consumer/fec_file_descriptor.h"
38#endif
Kelvin Zhangab3ce602021-02-24 14:46:40 -050039
40namespace chromeos_update_engine {
41using std::string;
42
43bool VerifiedSourceFd::OpenCurrentECCPartition() {
44 // No support for ECC for full payloads.
45 // Full payload should not have any opeartion that requires ECC partitions.
46 if (source_ecc_fd_)
47 return true;
48
49 if (source_ecc_open_failure_)
50 return false;
51
52#if USE_FEC
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070053 auto fd = std::make_shared<FecFileDescriptor>();
Kelvin Zhangab3ce602021-02-24 14:46:40 -050054 if (!fd->Open(source_path_.c_str(), O_RDONLY, 0)) {
55 PLOG(ERROR) << "Unable to open ECC source partition " << source_path_;
56 source_ecc_open_failure_ = true;
57 return false;
58 }
59 source_ecc_fd_ = fd;
60#else
61 // No support for ECC compiled.
62 source_ecc_open_failure_ = true;
63#endif // USE_FEC
64
65 return !source_ecc_open_failure_;
66}
67
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070068bool VerifiedSourceFd::WriteBackCorrectedSourceBlocks(
69 const std::vector<unsigned char>& source_data,
70 const google::protobuf::RepeatedPtrField<Extent>& extents) {
Kelvin Zhangcf299152023-06-16 10:58:40 -070071 utils::SetBlockDeviceReadOnly(source_path_, false);
72 DEFER {
73 utils::SetBlockDeviceReadOnly(source_path_, true);
74 };
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070075 auto fd = std::make_shared<EintrSafeFileDescriptor>();
76 TEST_AND_RETURN_FALSE_ERRNO(fd->Open(source_path_.c_str(), O_RDWR));
77 DirectExtentWriter writer(fd);
78 TEST_AND_RETURN_FALSE(writer.Init(extents, block_size_));
Kelvin Zhangcf299152023-06-16 10:58:40 -070079 TEST_AND_RETURN_FALSE(writer.Write(source_data.data(), source_data.size()));
80 return true;
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070081}
82
Kelvin Zhangab3ce602021-02-24 14:46:40 -050083FileDescriptorPtr VerifiedSourceFd::ChooseSourceFD(
84 const InstallOperation& operation, ErrorCode* error) {
85 if (source_fd_ == nullptr) {
86 LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
87 return nullptr;
88 }
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -070089 if (error) {
90 *error = ErrorCode::kSuccess;
91 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -050092 if (!operation.has_src_sha256_hash()) {
93 // When the operation doesn't include a source hash, we attempt the error
94 // corrected device first since we can't verify the block in the raw device
95 // at this point, but we first need to make sure all extents are readable
96 // since the error corrected device can be shorter or not available.
97 if (OpenCurrentECCPartition() &&
98 fd_utils::ReadAndHashExtents(
99 source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -0700100 if (error) {
101 *error = ErrorCode::kDownloadOperationHashMissingError;
102 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500103 return source_ecc_fd_;
104 }
105 return source_fd_;
106 }
107
108 brillo::Blob source_hash;
109 brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
110 operation.src_sha256_hash().end());
Kelvin Zhang8b07db52024-07-24 09:13:25 -0700111 if (!fd_utils::ReadAndHashExtents(
112 source_fd_, operation.src_extents(), block_size_, &source_hash)) {
113 LOG(ERROR) << "Failed to compute hash for operation " << operation.type()
114 << " data offset: " << operation.data_offset();
115 if (error) {
116 *error = ErrorCode::kDownloadOperationHashVerificationError;
117 }
118 return nullptr;
119 }
120 if (source_hash == expected_source_hash) {
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500121 return source_fd_;
122 }
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -0700123 if (error) {
124 *error = ErrorCode::kDownloadOperationHashMismatch;
125 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500126 // We fall back to use the error corrected device if the hash of the raw
127 // device doesn't match or there was an error reading the source partition.
128 if (!OpenCurrentECCPartition()) {
129 // The following function call will return false since the source hash
130 // mismatches, but we still want to call it so it prints the appropriate
131 // log message.
132 PartitionWriter::ValidateSourceHash(
133 source_hash, operation, source_fd_, error);
134 return nullptr;
135 }
136 LOG(WARNING) << "Source hash from RAW device mismatched: found "
137 << base::HexEncode(source_hash.data(), source_hash.size())
138 << ", expected "
139 << base::HexEncode(expected_source_hash.data(),
140 expected_source_hash.size());
141
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -0700142 std::vector<unsigned char> source_data;
143 if (!utils::ReadExtents(
144 source_ecc_fd_, operation.src_extents(), &source_data, block_size_)) {
145 return nullptr;
146 }
147 if (!HashCalculator::RawHashOfData(source_data, &source_hash)) {
148 return nullptr;
149 }
150 if (PartitionWriter::ValidateSourceHash(
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500151 source_hash, operation, source_ecc_fd_, error)) {
152 source_ecc_recovered_failures_++;
Kelvin Zhang7dd5a5e2023-05-11 15:30:38 -0700153 if (WriteBackCorrectedSourceBlocks(source_data, operation.src_extents())) {
154 if (error) {
155 *error = ErrorCode::kSuccess;
156 }
157 return source_fd_;
158 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500159 return source_ecc_fd_;
160 }
161 return nullptr;
162}
163
164bool VerifiedSourceFd::Open() {
165 source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
166 if (source_fd_ == nullptr)
167 return false;
Kelvin Zhang8fffea92023-07-25 20:34:26 -0700168 if (!source_fd_->Open(source_path_.c_str(), O_RDONLY)) {
169 PLOG(ERROR) << "Failed to open " << source_path_;
170 }
Kelvin Zhangab3ce602021-02-24 14:46:40 -0500171 return true;
172}
173
174} // namespace chromeos_update_engine