| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 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 | // 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> | 
|  | 26 | #include <base/strings/string_util.h> | 
|  | 27 | #include <base/strings/stringprintf.h> | 
|  | 28 |  | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 29 | #include "update_engine/common/error_code.h" | 
|  | 30 | #include "update_engine/common/hash_calculator.h" | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 31 | #include "update_engine/common/utils.h" | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 32 | #include "update_engine/payload_consumer/extent_writer.h" | 
|  | 33 | #include "update_engine/payload_consumer/file_descriptor.h" | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 34 | #include "update_engine/payload_consumer/file_descriptor_utils.h" | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 35 | #include "update_engine/payload_consumer/partition_writer.h" | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 36 | #include "update_engine/update_metadata.pb.h" | 
|  | 37 | #if USE_FEC | 
|  | 38 | #include "update_engine/payload_consumer/fec_file_descriptor.h" | 
|  | 39 | #endif | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 40 |  | 
|  | 41 | namespace chromeos_update_engine { | 
|  | 42 | using std::string; | 
|  | 43 |  | 
|  | 44 | bool VerifiedSourceFd::OpenCurrentECCPartition() { | 
|  | 45 | // No support for ECC for full payloads. | 
|  | 46 | // Full payload should not have any opeartion that requires ECC partitions. | 
|  | 47 | if (source_ecc_fd_) | 
|  | 48 | return true; | 
|  | 49 |  | 
|  | 50 | if (source_ecc_open_failure_) | 
|  | 51 | return false; | 
|  | 52 |  | 
|  | 53 | #if USE_FEC | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 54 | auto fd = std::make_shared<FecFileDescriptor>(); | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 55 | if (!fd->Open(source_path_.c_str(), O_RDONLY, 0)) { | 
|  | 56 | PLOG(ERROR) << "Unable to open ECC source partition " << source_path_; | 
|  | 57 | source_ecc_open_failure_ = true; | 
|  | 58 | return false; | 
|  | 59 | } | 
|  | 60 | source_ecc_fd_ = fd; | 
|  | 61 | #else | 
|  | 62 | // No support for ECC compiled. | 
|  | 63 | source_ecc_open_failure_ = true; | 
|  | 64 | #endif  // USE_FEC | 
|  | 65 |  | 
|  | 66 | return !source_ecc_open_failure_; | 
|  | 67 | } | 
|  | 68 |  | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 69 | bool VerifiedSourceFd::WriteBackCorrectedSourceBlocks( | 
|  | 70 | const std::vector<unsigned char>& source_data, | 
|  | 71 | const google::protobuf::RepeatedPtrField<Extent>& extents) { | 
| Kelvin Zhang | cf29915 | 2023-06-16 10:58:40 -0700 | [diff] [blame] | 72 | utils::SetBlockDeviceReadOnly(source_path_, false); | 
|  | 73 | DEFER { | 
|  | 74 | utils::SetBlockDeviceReadOnly(source_path_, true); | 
|  | 75 | }; | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 76 | auto fd = std::make_shared<EintrSafeFileDescriptor>(); | 
|  | 77 | TEST_AND_RETURN_FALSE_ERRNO(fd->Open(source_path_.c_str(), O_RDWR)); | 
|  | 78 | DirectExtentWriter writer(fd); | 
|  | 79 | TEST_AND_RETURN_FALSE(writer.Init(extents, block_size_)); | 
| Kelvin Zhang | cf29915 | 2023-06-16 10:58:40 -0700 | [diff] [blame] | 80 | TEST_AND_RETURN_FALSE(writer.Write(source_data.data(), source_data.size())); | 
|  | 81 | return true; | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 82 | } | 
|  | 83 |  | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 84 | FileDescriptorPtr VerifiedSourceFd::ChooseSourceFD( | 
|  | 85 | const InstallOperation& operation, ErrorCode* error) { | 
|  | 86 | if (source_fd_ == nullptr) { | 
|  | 87 | LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr"; | 
|  | 88 | return nullptr; | 
|  | 89 | } | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 90 | if (error) { | 
|  | 91 | *error = ErrorCode::kSuccess; | 
|  | 92 | } | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 93 | if (!operation.has_src_sha256_hash()) { | 
|  | 94 | // When the operation doesn't include a source hash, we attempt the error | 
|  | 95 | // corrected device first since we can't verify the block in the raw device | 
|  | 96 | // at this point, but we first need to make sure all extents are readable | 
|  | 97 | // since the error corrected device can be shorter or not available. | 
|  | 98 | if (OpenCurrentECCPartition() && | 
|  | 99 | fd_utils::ReadAndHashExtents( | 
|  | 100 | source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) { | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 101 | if (error) { | 
|  | 102 | *error = ErrorCode::kDownloadOperationHashMissingError; | 
|  | 103 | } | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 104 | return source_ecc_fd_; | 
|  | 105 | } | 
|  | 106 | return source_fd_; | 
|  | 107 | } | 
|  | 108 |  | 
|  | 109 | brillo::Blob source_hash; | 
|  | 110 | brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(), | 
|  | 111 | operation.src_sha256_hash().end()); | 
|  | 112 | if (fd_utils::ReadAndHashExtents( | 
|  | 113 | source_fd_, operation.src_extents(), block_size_, &source_hash) && | 
|  | 114 | source_hash == expected_source_hash) { | 
|  | 115 | return source_fd_; | 
|  | 116 | } | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 117 | if (error) { | 
|  | 118 | *error = ErrorCode::kDownloadOperationHashMismatch; | 
|  | 119 | } | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 120 | // We fall back to use the error corrected device if the hash of the raw | 
|  | 121 | // device doesn't match or there was an error reading the source partition. | 
|  | 122 | if (!OpenCurrentECCPartition()) { | 
|  | 123 | // The following function call will return false since the source hash | 
|  | 124 | // mismatches, but we still want to call it so it prints the appropriate | 
|  | 125 | // log message. | 
|  | 126 | PartitionWriter::ValidateSourceHash( | 
|  | 127 | source_hash, operation, source_fd_, error); | 
|  | 128 | return nullptr; | 
|  | 129 | } | 
|  | 130 | LOG(WARNING) << "Source hash from RAW device mismatched: found " | 
|  | 131 | << base::HexEncode(source_hash.data(), source_hash.size()) | 
|  | 132 | << ", expected " | 
|  | 133 | << base::HexEncode(expected_source_hash.data(), | 
|  | 134 | expected_source_hash.size()); | 
|  | 135 |  | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 136 | std::vector<unsigned char> source_data; | 
|  | 137 | if (!utils::ReadExtents( | 
|  | 138 | source_ecc_fd_, operation.src_extents(), &source_data, block_size_)) { | 
|  | 139 | return nullptr; | 
|  | 140 | } | 
|  | 141 | if (!HashCalculator::RawHashOfData(source_data, &source_hash)) { | 
|  | 142 | return nullptr; | 
|  | 143 | } | 
|  | 144 | if (PartitionWriter::ValidateSourceHash( | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 145 | source_hash, operation, source_ecc_fd_, error)) { | 
|  | 146 | source_ecc_recovered_failures_++; | 
| Kelvin Zhang | 7dd5a5e | 2023-05-11 15:30:38 -0700 | [diff] [blame] | 147 | if (WriteBackCorrectedSourceBlocks(source_data, operation.src_extents())) { | 
|  | 148 | if (error) { | 
|  | 149 | *error = ErrorCode::kSuccess; | 
|  | 150 | } | 
|  | 151 | return source_fd_; | 
|  | 152 | } | 
| Kelvin Zhang | ab3ce60 | 2021-02-24 14:46:40 -0500 | [diff] [blame] | 153 | return source_ecc_fd_; | 
|  | 154 | } | 
|  | 155 | return nullptr; | 
|  | 156 | } | 
|  | 157 |  | 
|  | 158 | bool VerifiedSourceFd::Open() { | 
|  | 159 | source_fd_ = std::make_shared<EintrSafeFileDescriptor>(); | 
|  | 160 | if (source_fd_ == nullptr) | 
|  | 161 | return false; | 
|  | 162 | TEST_AND_RETURN_FALSE_ERRNO(source_fd_->Open(source_path_.c_str(), O_RDONLY)); | 
|  | 163 | return true; | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | }  // namespace chromeos_update_engine |