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 
29 #include "update_engine/common/error_code.h"
30 #include "update_engine/common/hash_calculator.h"
31 #include "update_engine/common/utils.h"
32 #include "update_engine/payload_consumer/extent_writer.h"
33 #include "update_engine/payload_consumer/file_descriptor.h"
34 #include "update_engine/payload_consumer/file_descriptor_utils.h"
35 #include "update_engine/payload_consumer/partition_writer.h"
36 #include "update_engine/update_metadata.pb.h"
37 #if USE_FEC
38 #include "update_engine/payload_consumer/fec_file_descriptor.h"
39 #endif
40 
41 namespace chromeos_update_engine {
42 using std::string;
43 
OpenCurrentECCPartition()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
54   auto fd = std::make_shared<FecFileDescriptor>();
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 
WriteBackCorrectedSourceBlocks(const std::vector<unsigned char> & source_data,const google::protobuf::RepeatedPtrField<Extent> & extents)69 bool VerifiedSourceFd::WriteBackCorrectedSourceBlocks(
70     const std::vector<unsigned char>& source_data,
71     const google::protobuf::RepeatedPtrField<Extent>& extents) {
72   utils::SetBlockDeviceReadOnly(source_path_, false);
73   DEFER {
74     utils::SetBlockDeviceReadOnly(source_path_, true);
75   };
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_));
80   TEST_AND_RETURN_FALSE(writer.Write(source_data.data(), source_data.size()));
81   return true;
82 }
83 
ChooseSourceFD(const InstallOperation & operation,ErrorCode * error)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   }
90   if (error) {
91     *error = ErrorCode::kSuccess;
92   }
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)) {
101       if (error) {
102         *error = ErrorCode::kDownloadOperationHashMissingError;
103       }
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   }
117   if (error) {
118     *error = ErrorCode::kDownloadOperationHashMismatch;
119   }
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 
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(
145           source_hash, operation, source_ecc_fd_, error)) {
146     source_ecc_recovered_failures_++;
147     if (WriteBackCorrectedSourceBlocks(source_data, operation.src_extents())) {
148       if (error) {
149         *error = ErrorCode::kSuccess;
150       }
151       return source_fd_;
152     }
153     return source_ecc_fd_;
154   }
155   return nullptr;
156 }
157 
Open()158 bool VerifiedSourceFd::Open() {
159   source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
160   if (source_fd_ == nullptr)
161     return false;
162   if (!source_fd_->Open(source_path_.c_str(), O_RDONLY)) {
163     PLOG(ERROR) << "Failed to open " << source_path_;
164   }
165   return true;
166 }
167 
168 }  // namespace chromeos_update_engine
169