| Alex Deymo | aea4c1c | 2015-08-19 20:24:43 -0700 | [diff] [blame] | 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 | // | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 16 |  | 
|  | 17 | #include "update_engine/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 |  | 
| Alex Deymo | 20c9920 | 2015-07-09 16:14:16 -0700 | [diff] [blame] | 28 | #include <base/bind.h> | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 29 | #include <chromeos/streams/file_stream.h> | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 30 |  | 
| Alex Deymo | 763e7db | 2015-08-27 21:08:08 -0700 | [diff] [blame] | 31 | #include "update_engine/boot_control_interface.h" | 
| Sen Jiang | 70a6ab0 | 2015-08-28 13:23:27 -0700 | [diff] [blame] | 32 | #include "update_engine/payload_constants.h" | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 33 | #include "update_engine/system_state.h" | 
|  | 34 | #include "update_engine/utils.h" | 
|  | 35 |  | 
|  | 36 | using std::string; | 
|  | 37 |  | 
|  | 38 | namespace chromeos_update_engine { | 
|  | 39 |  | 
|  | 40 | namespace { | 
| Alex Deymo | 20c9920 | 2015-07-09 16:14:16 -0700 | [diff] [blame] | 41 | const off_t kReadFileBufferSize = 128 * 1024; | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 42 | }  // namespace | 
|  | 43 |  | 
| Alex Deymo | 763e7db | 2015-08-27 21:08:08 -0700 | [diff] [blame] | 44 | string PartitionTypeToString(const PartitionType partition_type) { | 
|  | 45 | // TODO(deymo): The PartitionType class should be replaced with just the | 
|  | 46 | // string name that comes from the payload. This function should be deleted | 
|  | 47 | // then. | 
|  | 48 | switch (partition_type) { | 
|  | 49 | case PartitionType::kRootfs: | 
|  | 50 | case PartitionType::kSourceRootfs: | 
|  | 51 | return kLegacyPartitionNameRoot; | 
|  | 52 | case PartitionType::kKernel: | 
|  | 53 | case PartitionType::kSourceKernel: | 
|  | 54 | return kLegacyPartitionNameKernel; | 
|  | 55 | } | 
|  | 56 | return "<unknown>"; | 
|  | 57 | } | 
|  | 58 |  | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 59 | FilesystemVerifierAction::FilesystemVerifierAction( | 
|  | 60 | SystemState* system_state, | 
|  | 61 | PartitionType partition_type) | 
|  | 62 | : partition_type_(partition_type), | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 63 | remaining_size_(kint64max), | 
|  | 64 | system_state_(system_state) {} | 
|  | 65 |  | 
|  | 66 | void FilesystemVerifierAction::PerformAction() { | 
|  | 67 | // Will tell the ActionProcessor we've failed if we return. | 
|  | 68 | ScopedActionCompleter abort_action_completer(processor_, this); | 
|  | 69 |  | 
|  | 70 | if (!HasInputObject()) { | 
|  | 71 | LOG(ERROR) << "FilesystemVerifierAction missing input object."; | 
|  | 72 | return; | 
|  | 73 | } | 
|  | 74 | install_plan_ = GetInputObject(); | 
|  | 75 |  | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 76 | if (install_plan_.is_full_update && | 
|  | 77 | (partition_type_ == PartitionType::kSourceRootfs || | 
|  | 78 | partition_type_ == PartitionType::kSourceKernel)) { | 
|  | 79 | // No hash verification needed. Done! | 
|  | 80 | LOG(INFO) << "filesystem verifying skipped on full update."; | 
|  | 81 | if (HasOutputPipe()) | 
|  | 82 | SetOutputObject(install_plan_); | 
|  | 83 | abort_action_completer.set_code(ErrorCode::kSuccess); | 
|  | 84 | return; | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | string target_path; | 
| Alex Deymo | 763e7db | 2015-08-27 21:08:08 -0700 | [diff] [blame] | 88 | string partition_name = PartitionTypeToString(partition_type_); | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 89 | switch (partition_type_) { | 
|  | 90 | case PartitionType::kRootfs: | 
|  | 91 | target_path = install_plan_.install_path; | 
|  | 92 | if (target_path.empty()) { | 
| Alex Deymo | 763e7db | 2015-08-27 21:08:08 -0700 | [diff] [blame] | 93 | system_state_->boot_control()->GetPartitionDevice( | 
|  | 94 | partition_name, install_plan_.target_slot, &target_path); | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 95 | } | 
|  | 96 | break; | 
|  | 97 | case PartitionType::kKernel: | 
|  | 98 | target_path = install_plan_.kernel_install_path; | 
|  | 99 | if (target_path.empty()) { | 
| Alex Deymo | 763e7db | 2015-08-27 21:08:08 -0700 | [diff] [blame] | 100 | system_state_->boot_control()->GetPartitionDevice( | 
|  | 101 | partition_name, install_plan_.target_slot, &target_path); | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 102 | } | 
|  | 103 | break; | 
|  | 104 | case PartitionType::kSourceRootfs: | 
| Alex Deymo | 763e7db | 2015-08-27 21:08:08 -0700 | [diff] [blame] | 105 | target_path = install_plan_.source_path; | 
|  | 106 | if (target_path.empty()) { | 
|  | 107 | system_state_->boot_control()->GetPartitionDevice( | 
|  | 108 | partition_name, install_plan_.source_slot, &target_path); | 
|  | 109 | } | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 110 | break; | 
|  | 111 | case PartitionType::kSourceKernel: | 
| Alex Deymo | 763e7db | 2015-08-27 21:08:08 -0700 | [diff] [blame] | 112 | target_path = install_plan_.kernel_source_path; | 
|  | 113 | if (target_path.empty()) { | 
|  | 114 | system_state_->boot_control()->GetPartitionDevice( | 
|  | 115 | partition_name, install_plan_.source_slot, &target_path); | 
|  | 116 | } | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 117 | break; | 
|  | 118 | } | 
|  | 119 |  | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 120 | chromeos::ErrorPtr error; | 
|  | 121 | src_stream_ = chromeos::FileStream::Open( | 
|  | 122 | base::FilePath(target_path), | 
|  | 123 | chromeos::Stream::AccessMode::READ, | 
|  | 124 | chromeos::FileStream::Disposition::OPEN_EXISTING, | 
|  | 125 | &error); | 
|  | 126 |  | 
|  | 127 | if (!src_stream_) { | 
|  | 128 | LOG(ERROR) << "Unable to open " << target_path << " for reading"; | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 129 | return; | 
|  | 130 | } | 
|  | 131 |  | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 132 | DetermineFilesystemSize(target_path); | 
| Alex Deymo | 20c9920 | 2015-07-09 16:14:16 -0700 | [diff] [blame] | 133 | buffer_.resize(kReadFileBufferSize); | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 134 |  | 
|  | 135 | // Start the first read. | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 136 | ScheduleRead(); | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 137 |  | 
|  | 138 | abort_action_completer.set_should_complete(false); | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | void FilesystemVerifierAction::TerminateProcessing() { | 
| Alex Deymo | 20c9920 | 2015-07-09 16:14:16 -0700 | [diff] [blame] | 142 | cancelled_ = true; | 
|  | 143 | Cleanup(ErrorCode::kSuccess);  // error code is ignored if canceled_ is true. | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 144 | } | 
|  | 145 |  | 
|  | 146 | bool FilesystemVerifierAction::IsCleanupPending() const { | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 147 | return src_stream_ != nullptr; | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 148 | } | 
|  | 149 |  | 
|  | 150 | void FilesystemVerifierAction::Cleanup(ErrorCode code) { | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 151 | src_stream_.reset(); | 
| Alex Deymo | 20c9920 | 2015-07-09 16:14:16 -0700 | [diff] [blame] | 152 | // This memory is not used anymore. | 
|  | 153 | buffer_.clear(); | 
|  | 154 |  | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 155 | if (cancelled_) | 
|  | 156 | return; | 
|  | 157 | if (code == ErrorCode::kSuccess && HasOutputPipe()) | 
|  | 158 | SetOutputObject(install_plan_); | 
|  | 159 | processor_->ActionComplete(this, code); | 
|  | 160 | } | 
|  | 161 |  | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 162 | void FilesystemVerifierAction::ScheduleRead() { | 
| Alex Deymo | 20c9920 | 2015-07-09 16:14:16 -0700 | [diff] [blame] | 163 | size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()), | 
|  | 164 | remaining_size_); | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 165 | if (!bytes_to_read) { | 
|  | 166 | OnReadDoneCallback(0); | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 167 | return; | 
|  | 168 | } | 
|  | 169 |  | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 170 | bool read_async_ok = src_stream_->ReadAsync( | 
|  | 171 | buffer_.data(), | 
|  | 172 | bytes_to_read, | 
|  | 173 | base::Bind(&FilesystemVerifierAction::OnReadDoneCallback, | 
|  | 174 | base::Unretained(this)), | 
|  | 175 | base::Bind(&FilesystemVerifierAction::OnReadErrorCallback, | 
|  | 176 | base::Unretained(this)), | 
|  | 177 | nullptr); | 
|  | 178 |  | 
|  | 179 | if (!read_async_ok) { | 
|  | 180 | LOG(ERROR) << "Unable to schedule an asynchronous read from the stream."; | 
|  | 181 | Cleanup(ErrorCode::kError); | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 182 | } | 
|  | 183 | } | 
|  | 184 |  | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 185 | void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) { | 
|  | 186 | if (bytes_read == 0) { | 
|  | 187 | read_done_ = true; | 
|  | 188 | } else { | 
|  | 189 | remaining_size_ -= bytes_read; | 
|  | 190 | CHECK(!read_done_); | 
|  | 191 | if (!hasher_.Update(buffer_.data(), bytes_read)) { | 
|  | 192 | LOG(ERROR) << "Unable to update the hash."; | 
|  | 193 | Cleanup(ErrorCode::kError); | 
|  | 194 | return; | 
|  | 195 | } | 
|  | 196 | } | 
|  | 197 |  | 
|  | 198 | // We either terminate the action or have more data to read. | 
|  | 199 | if (!CheckTerminationConditions()) | 
|  | 200 | ScheduleRead(); | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | void FilesystemVerifierAction::OnReadErrorCallback( | 
|  | 204 | const chromeos::Error* error) { | 
|  | 205 | // TODO(deymo): Transform the read-error into an specific ErrorCode. | 
|  | 206 | LOG(ERROR) << "Asynchronous read failed."; | 
|  | 207 | Cleanup(ErrorCode::kError); | 
|  | 208 | } | 
|  | 209 |  | 
|  | 210 | bool FilesystemVerifierAction::CheckTerminationConditions() { | 
|  | 211 | if (cancelled_) { | 
|  | 212 | Cleanup(ErrorCode::kError); | 
|  | 213 | return true; | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 | if (!read_done_) | 
|  | 217 | return false; | 
|  | 218 |  | 
|  | 219 | // We're done! | 
|  | 220 | ErrorCode code = ErrorCode::kSuccess; | 
|  | 221 | if (hasher_.Finalize()) { | 
|  | 222 | LOG(INFO) << "Hash: " << hasher_.hash(); | 
|  | 223 | switch (partition_type_) { | 
|  | 224 | case PartitionType::kRootfs: | 
|  | 225 | if (install_plan_.rootfs_hash != hasher_.raw_hash()) { | 
|  | 226 | code = ErrorCode::kNewRootfsVerificationError; | 
|  | 227 | LOG(ERROR) << "New rootfs verification failed."; | 
|  | 228 | } | 
|  | 229 | break; | 
|  | 230 | case PartitionType::kKernel: | 
|  | 231 | if (install_plan_.kernel_hash != hasher_.raw_hash()) { | 
|  | 232 | code = ErrorCode::kNewKernelVerificationError; | 
|  | 233 | LOG(ERROR) << "New kernel verification failed."; | 
|  | 234 | } | 
|  | 235 | break; | 
|  | 236 | case PartitionType::kSourceRootfs: | 
|  | 237 | install_plan_.source_rootfs_hash = hasher_.raw_hash(); | 
|  | 238 | break; | 
|  | 239 | case PartitionType::kSourceKernel: | 
|  | 240 | install_plan_.source_kernel_hash = hasher_.raw_hash(); | 
|  | 241 | break; | 
|  | 242 | } | 
|  | 243 | } else { | 
|  | 244 | LOG(ERROR) << "Unable to finalize the hash."; | 
|  | 245 | code = ErrorCode::kError; | 
|  | 246 | } | 
|  | 247 | Cleanup(code); | 
|  | 248 | return true; | 
|  | 249 | } | 
|  | 250 |  | 
| Alex Deymo | 763e7db | 2015-08-27 21:08:08 -0700 | [diff] [blame] | 251 | void FilesystemVerifierAction::DetermineFilesystemSize(const string& path) { | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 252 | switch (partition_type_) { | 
|  | 253 | case PartitionType::kRootfs: | 
|  | 254 | remaining_size_ = install_plan_.rootfs_size; | 
|  | 255 | LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes."; | 
|  | 256 | break; | 
|  | 257 | case PartitionType::kKernel: | 
|  | 258 | remaining_size_ = install_plan_.kernel_size; | 
|  | 259 | LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes."; | 
|  | 260 | break; | 
|  | 261 | case PartitionType::kSourceRootfs: | 
|  | 262 | { | 
|  | 263 | int block_count = 0, block_size = 0; | 
| Alex Deymo | b9e8e26 | 2015-08-03 20:23:03 -0700 | [diff] [blame] | 264 | if (utils::GetFilesystemSize(path, &block_count, &block_size)) { | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 265 | remaining_size_ = static_cast<int64_t>(block_count) * block_size; | 
|  | 266 | LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes (" | 
|  | 267 | << block_count << "x" << block_size << ")."; | 
|  | 268 | } | 
|  | 269 | } | 
|  | 270 | break; | 
|  | 271 | default: | 
|  | 272 | break; | 
|  | 273 | } | 
| Allie Wood | eb9e6d8 | 2015-04-17 13:55:30 -0700 | [diff] [blame] | 274 | } | 
|  | 275 |  | 
|  | 276 | }  // namespace chromeos_update_engine |