Home | History | Annotate | Download | only in payload_consumer
      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