Home | History | Annotate | Download | only in common
      1 //
      2 // Copyright (C) 2016 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/common/file_fetcher.h"
     18 
     19 #include <algorithm>
     20 #include <string>
     21 
     22 #include <base/bind.h>
     23 #include <base/format_macros.h>
     24 #include <base/location.h>
     25 #include <base/logging.h>
     26 #include <base/strings/string_util.h>
     27 #include <base/strings/stringprintf.h>
     28 #include <brillo/streams/file_stream.h>
     29 
     30 #include "update_engine/common/hardware_interface.h"
     31 #include "update_engine/common/platform_constants.h"
     32 
     33 using std::string;
     34 
     35 namespace {
     36 
     37 size_t kReadBufferSize = 16 * 1024;
     38 
     39 }  // namespace
     40 
     41 namespace chromeos_update_engine {
     42 
     43 // static
     44 bool FileFetcher::SupportedUrl(const string& url) {
     45   // Note that we require the file path to start with a "/".
     46   return base::StartsWith(
     47       url, "file:///", base::CompareCase::INSENSITIVE_ASCII);
     48 }
     49 
     50 FileFetcher::~FileFetcher() {
     51   LOG_IF(ERROR, transfer_in_progress_)
     52       << "Destroying the fetcher while a transfer is in progress.";
     53   CleanUp();
     54 }
     55 
     56 // Begins the transfer, which must not have already been started.
     57 void FileFetcher::BeginTransfer(const string& url) {
     58   CHECK(!transfer_in_progress_);
     59 
     60   if (!SupportedUrl(url)) {
     61     LOG(ERROR) << "Unsupported file URL: " << url;
     62     // No HTTP error code when the URL is not supported.
     63     http_response_code_ = 0;
     64     CleanUp();
     65     if (delegate_)
     66       delegate_->TransferComplete(this, false);
     67     return;
     68   }
     69 
     70   string file_path = url.substr(strlen("file://"));
     71   stream_ =
     72       brillo::FileStream::Open(base::FilePath(file_path),
     73                                brillo::Stream::AccessMode::READ,
     74                                brillo::FileStream::Disposition::OPEN_EXISTING,
     75                                nullptr);
     76 
     77   if (!stream_) {
     78     LOG(ERROR) << "Couldn't open " << file_path;
     79     http_response_code_ = kHttpResponseNotFound;
     80     CleanUp();
     81     if (delegate_)
     82       delegate_->TransferComplete(this, false);
     83     return;
     84   }
     85   http_response_code_ = kHttpResponseOk;
     86 
     87   if (offset_)
     88     stream_->SetPosition(offset_, nullptr);
     89   bytes_copied_ = 0;
     90   transfer_in_progress_ = true;
     91   ScheduleRead();
     92 }
     93 
     94 void FileFetcher::TerminateTransfer() {
     95   CleanUp();
     96   if (delegate_) {
     97     // Note that after the callback returns this object may be destroyed.
     98     delegate_->TransferTerminated(this);
     99   }
    100 }
    101 
    102 void FileFetcher::ScheduleRead() {
    103   if (transfer_paused_ || ongoing_read_ || !transfer_in_progress_)
    104     return;
    105 
    106   buffer_.resize(kReadBufferSize);
    107   size_t bytes_to_read = buffer_.size();
    108   if (data_length_ >= 0) {
    109     bytes_to_read = std::min(static_cast<uint64_t>(bytes_to_read),
    110                              data_length_ - bytes_copied_);
    111   }
    112 
    113   if (!bytes_to_read) {
    114     OnReadDoneCallback(0);
    115     return;
    116   }
    117 
    118   ongoing_read_ = stream_->ReadAsync(
    119       buffer_.data(),
    120       bytes_to_read,
    121       base::Bind(&FileFetcher::OnReadDoneCallback, base::Unretained(this)),
    122       base::Bind(&FileFetcher::OnReadErrorCallback, base::Unretained(this)),
    123       nullptr);
    124 
    125   if (!ongoing_read_) {
    126     LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
    127     CleanUp();
    128     if (delegate_)
    129       delegate_->TransferComplete(this, false);
    130   }
    131 }
    132 
    133 void FileFetcher::OnReadDoneCallback(size_t bytes_read) {
    134   ongoing_read_ = false;
    135   if (bytes_read == 0) {
    136     CleanUp();
    137     if (delegate_)
    138       delegate_->TransferComplete(this, true);
    139   } else {
    140     bytes_copied_ += bytes_read;
    141     if (delegate_)
    142       delegate_->ReceivedBytes(this, buffer_.data(), bytes_read);
    143     ScheduleRead();
    144   }
    145 }
    146 
    147 void FileFetcher::OnReadErrorCallback(const brillo::Error* error) {
    148   LOG(ERROR) << "Asynchronous read failed: " << error->GetMessage();
    149   CleanUp();
    150   if (delegate_)
    151     delegate_->TransferComplete(this, false);
    152 }
    153 
    154 void FileFetcher::Pause() {
    155   if (transfer_paused_) {
    156     LOG(ERROR) << "Fetcher already paused.";
    157     return;
    158   }
    159   transfer_paused_ = true;
    160 }
    161 
    162 void FileFetcher::Unpause() {
    163   if (!transfer_paused_) {
    164     LOG(ERROR) << "Resume attempted when fetcher not paused.";
    165     return;
    166   }
    167   transfer_paused_ = false;
    168   ScheduleRead();
    169 }
    170 
    171 void FileFetcher::CleanUp() {
    172   if (stream_) {
    173     stream_->CancelPendingAsyncOperations();
    174     stream_->CloseBlocking(nullptr);
    175     stream_.reset();
    176   }
    177   // Destroying the |stream_| releases the callback, so we don't have any
    178   // ongoing read at this point.
    179   ongoing_read_ = false;
    180   buffer_ = brillo::Blob();
    181 
    182   transfer_in_progress_ = false;
    183   transfer_paused_ = false;
    184 }
    185 
    186 }  // namespace chromeos_update_engine
    187