Home | History | Annotate | Download | only in payload_consumer
      1 //
      2 // Copyright (C) 2011 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/download_action.h"
     18 
     19 #include <errno.h>
     20 
     21 #include <algorithm>
     22 #include <string>
     23 #include <vector>
     24 
     25 #include <base/files/file_path.h>
     26 #include <base/strings/stringprintf.h>
     27 
     28 #include "update_engine/common/action_pipe.h"
     29 #include "update_engine/common/boot_control_interface.h"
     30 #include "update_engine/common/utils.h"
     31 #include "update_engine/omaha_request_params.h"
     32 #include "update_engine/p2p_manager.h"
     33 #include "update_engine/payload_state_interface.h"
     34 
     35 using base::FilePath;
     36 using std::string;
     37 using std::vector;
     38 
     39 namespace chromeos_update_engine {
     40 
     41 DownloadAction::DownloadAction(PrefsInterface* prefs,
     42                                BootControlInterface* boot_control,
     43                                HardwareInterface* hardware,
     44                                SystemState* system_state,
     45                                HttpFetcher* http_fetcher)
     46     : prefs_(prefs),
     47       boot_control_(boot_control),
     48       hardware_(hardware),
     49       system_state_(system_state),
     50       http_fetcher_(http_fetcher),
     51       writer_(nullptr),
     52       code_(ErrorCode::kSuccess),
     53       delegate_(nullptr),
     54       bytes_received_(0),
     55       p2p_sharing_fd_(-1),
     56       p2p_visible_(true) {
     57 }
     58 
     59 DownloadAction::~DownloadAction() {}
     60 
     61 void DownloadAction::CloseP2PSharingFd(bool delete_p2p_file) {
     62   if (p2p_sharing_fd_ != -1) {
     63     if (close(p2p_sharing_fd_) != 0) {
     64       PLOG(ERROR) << "Error closing p2p sharing fd";
     65     }
     66     p2p_sharing_fd_ = -1;
     67   }
     68 
     69   if (delete_p2p_file) {
     70     FilePath path =
     71       system_state_->p2p_manager()->FileGetPath(p2p_file_id_);
     72     if (unlink(path.value().c_str()) != 0) {
     73       PLOG(ERROR) << "Error deleting p2p file " << path.value();
     74     } else {
     75       LOG(INFO) << "Deleted p2p file " << path.value();
     76     }
     77   }
     78 
     79   // Don't use p2p from this point onwards.
     80   p2p_file_id_.clear();
     81 }
     82 
     83 bool DownloadAction::SetupP2PSharingFd() {
     84   P2PManager *p2p_manager = system_state_->p2p_manager();
     85 
     86   if (!p2p_manager->FileShare(p2p_file_id_, install_plan_.payload_size)) {
     87     LOG(ERROR) << "Unable to share file via p2p";
     88     CloseP2PSharingFd(true);  // delete p2p file
     89     return false;
     90   }
     91 
     92   // File has already been created (and allocated, xattrs been
     93   // populated etc.) by FileShare() so just open it for writing.
     94   FilePath path = p2p_manager->FileGetPath(p2p_file_id_);
     95   p2p_sharing_fd_ = open(path.value().c_str(), O_WRONLY);
     96   if (p2p_sharing_fd_ == -1) {
     97     PLOG(ERROR) << "Error opening file " << path.value();
     98     CloseP2PSharingFd(true);  // Delete p2p file.
     99     return false;
    100   }
    101 
    102   // Ensure file to share is world-readable, otherwise
    103   // p2p-server and p2p-http-server can't access it.
    104   //
    105   // (Q: Why doesn't the file have mode 0644 already? A: Because
    106   // the process-wide umask is set to 0700 in main.cc.)
    107   if (fchmod(p2p_sharing_fd_, 0644) != 0) {
    108     PLOG(ERROR) << "Error setting mode 0644 on " << path.value();
    109     CloseP2PSharingFd(true);  // Delete p2p file.
    110     return false;
    111   }
    112 
    113   // All good.
    114   LOG(INFO) << "Writing payload contents to " << path.value();
    115   p2p_manager->FileGetVisible(p2p_file_id_, &p2p_visible_);
    116   return true;
    117 }
    118 
    119 void DownloadAction::WriteToP2PFile(const void* data,
    120                                     size_t length,
    121                                     off_t file_offset) {
    122   if (p2p_sharing_fd_ == -1) {
    123     if (!SetupP2PSharingFd())
    124       return;
    125   }
    126 
    127   // Check that the file is at least |file_offset| bytes long - if
    128   // it's not something is wrong and we must immediately delete the
    129   // file to avoid propagating this problem to other peers.
    130   //
    131   // How can this happen? It could be that we're resuming an update
    132   // after a system crash... in this case, it could be that
    133   //
    134   //  1. the p2p file didn't get properly synced to stable storage; or
    135   //  2. the file was deleted at bootup (it's in /var/cache after all); or
    136   //  3. other reasons
    137   off_t p2p_size = utils::FileSize(p2p_sharing_fd_);
    138   if (p2p_size < 0) {
    139     PLOG(ERROR) << "Error getting file status for p2p file";
    140     CloseP2PSharingFd(true);  // Delete p2p file.
    141     return;
    142   }
    143   if (p2p_size < file_offset) {
    144     LOG(ERROR) << "Wanting to write to file offset " << file_offset
    145                << " but existing p2p file is only " << p2p_size
    146                << " bytes.";
    147     CloseP2PSharingFd(true);  // Delete p2p file.
    148     return;
    149   }
    150 
    151   off_t cur_file_offset = lseek(p2p_sharing_fd_, file_offset, SEEK_SET);
    152   if (cur_file_offset != static_cast<off_t>(file_offset)) {
    153     PLOG(ERROR) << "Error seeking to position "
    154                 << file_offset << " in p2p file";
    155     CloseP2PSharingFd(true);  // Delete p2p file.
    156   } else {
    157     // OK, seeking worked, now write the data
    158     ssize_t bytes_written = write(p2p_sharing_fd_, data, length);
    159     if (bytes_written != static_cast<ssize_t>(length)) {
    160       PLOG(ERROR) << "Error writing "
    161                   << length << " bytes at file offset "
    162                   << file_offset << " in p2p file";
    163       CloseP2PSharingFd(true);  // Delete p2p file.
    164     }
    165   }
    166 }
    167 
    168 void DownloadAction::PerformAction() {
    169   http_fetcher_->set_delegate(this);
    170 
    171   // Get the InstallPlan and read it
    172   CHECK(HasInputObject());
    173   install_plan_ = GetInputObject();
    174   bytes_received_ = 0;
    175 
    176   install_plan_.Dump();
    177 
    178   LOG(INFO) << "Marking new slot as unbootable";
    179   if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) {
    180     LOG(WARNING) << "Unable to mark new slot "
    181                  << BootControlInterface::SlotName(install_plan_.target_slot)
    182                  << ". Proceeding with the update anyway.";
    183   }
    184 
    185   if (writer_) {
    186     LOG(INFO) << "Using writer for test.";
    187   } else {
    188     delta_performer_.reset(new DeltaPerformer(
    189         prefs_, boot_control_, hardware_, delegate_, &install_plan_));
    190     writer_ = delta_performer_.get();
    191   }
    192   download_active_ = true;
    193 
    194   if (system_state_ != nullptr) {
    195     const PayloadStateInterface* payload_state = system_state_->payload_state();
    196     string file_id = utils::CalculateP2PFileId(install_plan_.payload_hash,
    197                                                install_plan_.payload_size);
    198     if (payload_state->GetUsingP2PForSharing()) {
    199       // If we're sharing the update, store the file_id to convey
    200       // that we should write to the file.
    201       p2p_file_id_ = file_id;
    202       LOG(INFO) << "p2p file id: " << p2p_file_id_;
    203     } else {
    204       // Even if we're not sharing the update, it could be that
    205       // there's a partial file from a previous attempt with the same
    206       // hash. If this is the case, we NEED to clean it up otherwise
    207       // we're essentially timing out other peers downloading from us
    208       // (since we're never going to complete the file).
    209       FilePath path = system_state_->p2p_manager()->FileGetPath(file_id);
    210       if (!path.empty()) {
    211         if (unlink(path.value().c_str()) != 0) {
    212           PLOG(ERROR) << "Error deleting p2p file " << path.value();
    213         } else {
    214           LOG(INFO) << "Deleting partial p2p file " << path.value()
    215                     << " since we're not using p2p to share.";
    216         }
    217       }
    218     }
    219 
    220     // Tweak timeouts on the HTTP fetcher if we're downloading from a
    221     // local peer.
    222     if (payload_state->GetUsingP2PForDownloading() &&
    223         payload_state->GetP2PUrl() == install_plan_.download_url) {
    224       LOG(INFO) << "Tweaking HTTP fetcher since we're downloading via p2p";
    225       http_fetcher_->set_low_speed_limit(kDownloadP2PLowSpeedLimitBps,
    226                                          kDownloadP2PLowSpeedTimeSeconds);
    227       http_fetcher_->set_max_retry_count(kDownloadP2PMaxRetryCount);
    228       http_fetcher_->set_connect_timeout(kDownloadP2PConnectTimeoutSeconds);
    229     }
    230   }
    231 
    232   http_fetcher_->BeginTransfer(install_plan_.download_url);
    233 }
    234 
    235 void DownloadAction::SuspendAction() {
    236   http_fetcher_->Pause();
    237 }
    238 
    239 void DownloadAction::ResumeAction() {
    240   http_fetcher_->Unpause();
    241 }
    242 
    243 void DownloadAction::TerminateProcessing() {
    244   if (writer_) {
    245     writer_->Close();
    246     writer_ = nullptr;
    247   }
    248   download_active_ = false;
    249   CloseP2PSharingFd(false);  // Keep p2p file.
    250   // Terminates the transfer. The action is terminated, if necessary, when the
    251   // TransferTerminated callback is received.
    252   http_fetcher_->TerminateTransfer();
    253 }
    254 
    255 void DownloadAction::SeekToOffset(off_t offset) {
    256   bytes_received_ = offset;
    257 }
    258 
    259 void DownloadAction::ReceivedBytes(HttpFetcher* fetcher,
    260                                    const void* bytes,
    261                                    size_t length) {
    262   // Note that bytes_received_ is the current offset.
    263   if (!p2p_file_id_.empty()) {
    264     WriteToP2PFile(bytes, length, bytes_received_);
    265   }
    266 
    267   bytes_received_ += length;
    268   if (delegate_ && download_active_) {
    269     delegate_->BytesReceived(
    270         length, bytes_received_, install_plan_.payload_size);
    271   }
    272   if (writer_ && !writer_->Write(bytes, length, &code_)) {
    273     LOG(ERROR) << "Error " << code_ << " in DeltaPerformer's Write method when "
    274                << "processing the received payload -- Terminating processing";
    275     // Delete p2p file, if applicable.
    276     if (!p2p_file_id_.empty())
    277       CloseP2PSharingFd(true);
    278     // Don't tell the action processor that the action is complete until we get
    279     // the TransferTerminated callback. Otherwise, this and the HTTP fetcher
    280     // objects may get destroyed before all callbacks are complete.
    281     TerminateProcessing();
    282     return;
    283   }
    284 
    285   // Call p2p_manager_->FileMakeVisible() when we've successfully
    286   // verified the manifest!
    287   if (!p2p_visible_ && system_state_ && delta_performer_.get() &&
    288       delta_performer_->IsManifestValid()) {
    289     LOG(INFO) << "Manifest has been validated. Making p2p file visible.";
    290     system_state_->p2p_manager()->FileMakeVisible(p2p_file_id_);
    291     p2p_visible_ = true;
    292   }
    293 }
    294 
    295 void DownloadAction::TransferComplete(HttpFetcher* fetcher, bool successful) {
    296   if (writer_) {
    297     LOG_IF(WARNING, writer_->Close() != 0) << "Error closing the writer.";
    298     writer_ = nullptr;
    299   }
    300   download_active_ = false;
    301   ErrorCode code =
    302       successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError;
    303   if (code == ErrorCode::kSuccess && delta_performer_.get()) {
    304     code = delta_performer_->VerifyPayload(install_plan_.payload_hash,
    305                                            install_plan_.payload_size);
    306     if (code != ErrorCode::kSuccess) {
    307       LOG(ERROR) << "Download of " << install_plan_.download_url
    308                  << " failed due to payload verification error.";
    309       // Delete p2p file, if applicable.
    310       if (!p2p_file_id_.empty())
    311         CloseP2PSharingFd(true);
    312     }
    313   }
    314 
    315   // Write the path to the output pipe if we're successful.
    316   if (code == ErrorCode::kSuccess && HasOutputPipe())
    317     SetOutputObject(install_plan_);
    318   processor_->ActionComplete(this, code);
    319 }
    320 
    321 void DownloadAction::TransferTerminated(HttpFetcher *fetcher) {
    322   if (code_ != ErrorCode::kSuccess) {
    323     processor_->ActionComplete(this, code_);
    324   }
    325 }
    326 
    327 }  // namespace chromeos_update_engine
    328