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