1 // 2 // Copyright (C) 2012 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 "update_engine/payload_consumer/filesystem_verifier_action.h" 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <sys/stat.h> 22 #include <sys/types.h> 23 24 #include <algorithm> 25 #include <cstdlib> 26 #include <string> 27 28 #include <base/bind.h> 29 #include <brillo/streams/file_stream.h> 30 31 #include "update_engine/common/boot_control_interface.h" 32 #include "update_engine/common/utils.h" 33 #include "update_engine/payload_consumer/delta_performer.h" 34 #include "update_engine/payload_consumer/payload_constants.h" 35 36 using std::string; 37 38 namespace chromeos_update_engine { 39 40 namespace { 41 const off_t kReadFileBufferSize = 128 * 1024; 42 } // namespace 43 44 FilesystemVerifierAction::FilesystemVerifierAction( 45 const BootControlInterface* boot_control, 46 VerifierMode verifier_mode) 47 : verifier_mode_(verifier_mode), 48 boot_control_(boot_control) {} 49 50 void FilesystemVerifierAction::PerformAction() { 51 // Will tell the ActionProcessor we've failed if we return. 52 ScopedActionCompleter abort_action_completer(processor_, this); 53 54 if (!HasInputObject()) { 55 LOG(ERROR) << "FilesystemVerifierAction missing input object."; 56 return; 57 } 58 install_plan_ = GetInputObject(); 59 60 // For delta updates (major version 1) we need to populate the source 61 // partition hash if not pre-populated. 62 if (install_plan_.payload_type == InstallPayloadType::kDelta && 63 install_plan_.partitions.empty() && 64 verifier_mode_ == VerifierMode::kComputeSourceHash && 65 DeltaPerformer::kSupportedMinorPayloadVersion < 66 kOpSrcHashMinorPayloadVersion) { 67 LOG(INFO) << "Using legacy partition names."; 68 InstallPlan::Partition part; 69 string part_path; 70 71 part.name = kLegacyPartitionNameRoot; 72 if (!boot_control_->GetPartitionDevice( 73 part.name, install_plan_.source_slot, &part_path)) 74 return; 75 int block_count = 0, block_size = 0; 76 if (utils::GetFilesystemSize(part_path, &block_count, &block_size)) { 77 part.source_size = static_cast<int64_t>(block_count) * block_size; 78 LOG(INFO) << "Partition " << part.name << " size: " << part.source_size 79 << " bytes (" << block_count << "x" << block_size << ")."; 80 } 81 install_plan_.partitions.push_back(part); 82 83 part.name = kLegacyPartitionNameKernel; 84 if (!boot_control_->GetPartitionDevice( 85 part.name, install_plan_.source_slot, &part_path)) 86 return; 87 off_t kernel_part_size = utils::FileSize(part_path); 88 if (kernel_part_size < 0) 89 return; 90 LOG(INFO) << "Partition " << part.name << " size: " << kernel_part_size 91 << " bytes."; 92 part.source_size = kernel_part_size; 93 install_plan_.partitions.push_back(part); 94 } 95 96 if (install_plan_.partitions.empty()) { 97 LOG(INFO) << "No partitions to verify."; 98 if (HasOutputPipe()) 99 SetOutputObject(install_plan_); 100 abort_action_completer.set_code(ErrorCode::kSuccess); 101 return; 102 } 103 104 StartPartitionHashing(); 105 abort_action_completer.set_should_complete(false); 106 } 107 108 void FilesystemVerifierAction::TerminateProcessing() { 109 cancelled_ = true; 110 Cleanup(ErrorCode::kSuccess); // error code is ignored if canceled_ is true. 111 } 112 113 bool FilesystemVerifierAction::IsCleanupPending() const { 114 return src_stream_ != nullptr; 115 } 116 117 void FilesystemVerifierAction::Cleanup(ErrorCode code) { 118 src_stream_.reset(); 119 // This memory is not used anymore. 120 buffer_.clear(); 121 122 if (cancelled_) 123 return; 124 if (code == ErrorCode::kSuccess && HasOutputPipe()) 125 SetOutputObject(install_plan_); 126 processor_->ActionComplete(this, code); 127 } 128 129 void FilesystemVerifierAction::StartPartitionHashing() { 130 if (partition_index_ == install_plan_.partitions.size()) { 131 // We never called this action with kVerifySourceHash directly, if we are in 132 // this mode, it means the target partition verification has failed, so we 133 // should set the error code to reflect the error in target. 134 if (verifier_mode_ == VerifierMode::kVerifySourceHash) 135 Cleanup(ErrorCode::kNewRootfsVerificationError); 136 else 137 Cleanup(ErrorCode::kSuccess); 138 return; 139 } 140 InstallPlan::Partition& partition = 141 install_plan_.partitions[partition_index_]; 142 143 string part_path; 144 switch (verifier_mode_) { 145 case VerifierMode::kComputeSourceHash: 146 case VerifierMode::kVerifySourceHash: 147 boot_control_->GetPartitionDevice( 148 partition.name, install_plan_.source_slot, &part_path); 149 remaining_size_ = partition.source_size; 150 break; 151 case VerifierMode::kVerifyTargetHash: 152 boot_control_->GetPartitionDevice( 153 partition.name, install_plan_.target_slot, &part_path); 154 remaining_size_ = partition.target_size; 155 break; 156 } 157 LOG(INFO) << "Hashing partition " << partition_index_ << " (" 158 << partition.name << ") on device " << part_path; 159 if (part_path.empty()) 160 return Cleanup(ErrorCode::kFilesystemVerifierError); 161 162 brillo::ErrorPtr error; 163 src_stream_ = brillo::FileStream::Open( 164 base::FilePath(part_path), 165 brillo::Stream::AccessMode::READ, 166 brillo::FileStream::Disposition::OPEN_EXISTING, 167 &error); 168 169 if (!src_stream_) { 170 LOG(ERROR) << "Unable to open " << part_path << " for reading"; 171 return Cleanup(ErrorCode::kFilesystemVerifierError); 172 } 173 174 buffer_.resize(kReadFileBufferSize); 175 read_done_ = false; 176 hasher_.reset(new HashCalculator()); 177 178 // Start the first read. 179 ScheduleRead(); 180 } 181 182 void FilesystemVerifierAction::ScheduleRead() { 183 size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()), 184 remaining_size_); 185 if (!bytes_to_read) { 186 OnReadDoneCallback(0); 187 return; 188 } 189 190 bool read_async_ok = src_stream_->ReadAsync( 191 buffer_.data(), 192 bytes_to_read, 193 base::Bind(&FilesystemVerifierAction::OnReadDoneCallback, 194 base::Unretained(this)), 195 base::Bind(&FilesystemVerifierAction::OnReadErrorCallback, 196 base::Unretained(this)), 197 nullptr); 198 199 if (!read_async_ok) { 200 LOG(ERROR) << "Unable to schedule an asynchronous read from the stream."; 201 Cleanup(ErrorCode::kError); 202 } 203 } 204 205 void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) { 206 if (bytes_read == 0) { 207 read_done_ = true; 208 } else { 209 remaining_size_ -= bytes_read; 210 CHECK(!read_done_); 211 if (!hasher_->Update(buffer_.data(), bytes_read)) { 212 LOG(ERROR) << "Unable to update the hash."; 213 Cleanup(ErrorCode::kError); 214 return; 215 } 216 } 217 218 // We either terminate the current partition or have more data to read. 219 if (cancelled_) 220 return Cleanup(ErrorCode::kError); 221 222 if (read_done_ || remaining_size_ == 0) { 223 if (remaining_size_ != 0) { 224 LOG(ERROR) << "Failed to read the remaining " << remaining_size_ 225 << " bytes from partition " 226 << install_plan_.partitions[partition_index_].name; 227 return Cleanup(ErrorCode::kFilesystemVerifierError); 228 } 229 return FinishPartitionHashing(); 230 } 231 ScheduleRead(); 232 } 233 234 void FilesystemVerifierAction::OnReadErrorCallback( 235 const brillo::Error* error) { 236 // TODO(deymo): Transform the read-error into an specific ErrorCode. 237 LOG(ERROR) << "Asynchronous read failed."; 238 Cleanup(ErrorCode::kError); 239 } 240 241 void FilesystemVerifierAction::FinishPartitionHashing() { 242 if (!hasher_->Finalize()) { 243 LOG(ERROR) << "Unable to finalize the hash."; 244 return Cleanup(ErrorCode::kError); 245 } 246 InstallPlan::Partition& partition = 247 install_plan_.partitions[partition_index_]; 248 LOG(INFO) << "Hash of " << partition.name << ": " << hasher_->hash(); 249 250 switch (verifier_mode_) { 251 case VerifierMode::kComputeSourceHash: 252 partition.source_hash = hasher_->raw_hash(); 253 partition_index_++; 254 break; 255 case VerifierMode::kVerifyTargetHash: 256 if (partition.target_hash != hasher_->raw_hash()) { 257 LOG(ERROR) << "New '" << partition.name 258 << "' partition verification failed."; 259 if (DeltaPerformer::kSupportedMinorPayloadVersion < 260 kOpSrcHashMinorPayloadVersion) 261 return Cleanup(ErrorCode::kNewRootfsVerificationError); 262 // If we support per-operation source hash, then we skipped source 263 // filesystem verification, now that the target partition does not 264 // match, we need to switch to kVerifySourceHash mode to check if it's 265 // because the source partition does not match either. 266 verifier_mode_ = VerifierMode::kVerifySourceHash; 267 partition_index_ = 0; 268 } else { 269 partition_index_++; 270 } 271 break; 272 case VerifierMode::kVerifySourceHash: 273 if (partition.source_hash != hasher_->raw_hash()) { 274 LOG(ERROR) << "Old '" << partition.name 275 << "' partition verification failed."; 276 return Cleanup(ErrorCode::kDownloadStateInitializationError); 277 } 278 partition_index_++; 279 break; 280 } 281 // Start hashing the next partition, if any. 282 hasher_.reset(); 283 buffer_.clear(); 284 src_stream_->CloseBlocking(nullptr); 285 StartPartitionHashing(); 286 } 287 288 } // namespace chromeos_update_engine 289