Home | History | Annotate | Download | only in download
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/download/base_file.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/format_macros.h"
      9 #include "base/logging.h"
     10 #include "base/stringprintf.h"
     11 #include "crypto/secure_hash.h"
     12 #include "net/base/file_stream.h"
     13 #include "net/base/net_errors.h"
     14 #include "chrome/browser/download/download_util.h"
     15 #include "content/browser/browser_thread.h"
     16 
     17 #if defined(OS_WIN)
     18 #include "chrome/common/win_safe_util.h"
     19 #elif defined(OS_MACOSX)
     20 #include "chrome/browser/cocoa/file_metadata.h"
     21 #endif
     22 
     23 BaseFile::BaseFile(const FilePath& full_path,
     24                    const GURL& source_url,
     25                    const GURL& referrer_url,
     26                    int64 received_bytes,
     27                    const linked_ptr<net::FileStream>& file_stream)
     28     : full_path_(full_path),
     29       source_url_(source_url),
     30       referrer_url_(referrer_url),
     31       file_stream_(file_stream),
     32       bytes_so_far_(received_bytes),
     33       power_save_blocker_(true),
     34       calculate_hash_(false),
     35       detached_(false) {
     36   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     37   memset(sha256_hash_, 0, sizeof(sha256_hash_));
     38 }
     39 
     40 BaseFile::~BaseFile() {
     41   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     42   if (detached_)
     43     Close();
     44   else
     45     Cancel();  // Will delete the file.
     46 }
     47 
     48 bool BaseFile::Initialize(bool calculate_hash) {
     49   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     50   DCHECK(!detached_);
     51 
     52   calculate_hash_ = calculate_hash;
     53 
     54   if (calculate_hash_)
     55     secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256));
     56 
     57   if (!full_path_.empty() ||
     58       download_util::CreateTemporaryFileForDownload(&full_path_))
     59     return Open();
     60   return false;
     61 }
     62 
     63 bool BaseFile::AppendDataToFile(const char* data, size_t data_len) {
     64   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     65   DCHECK(!detached_);
     66 
     67   if (!file_stream_.get())
     68     return false;
     69 
     70   // TODO(phajdan.jr): get rid of this check.
     71   if (data_len == 0)
     72     return true;
     73 
     74   bytes_so_far_ += data_len;
     75 
     76   // TODO(phajdan.jr): handle errors on file writes. http://crbug.com/58355
     77   size_t written = file_stream_->Write(data, data_len, NULL);
     78   if (written != data_len)
     79     return false;
     80 
     81   if (calculate_hash_)
     82     secure_hash_->Update(data, data_len);
     83 
     84   return true;
     85 }
     86 
     87 bool BaseFile::Rename(const FilePath& new_path) {
     88   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     89 
     90   // Save the information whether the download is in progress because
     91   // it will be overwritten by closing the file.
     92   bool saved_in_progress = in_progress();
     93 
     94   // If the new path is same as the old one, there is no need to perform the
     95   // following renaming logic.
     96   if (new_path == full_path_) {
     97     // Don't close the file if we're not done (finished or canceled).
     98     if (!saved_in_progress)
     99       Close();
    100 
    101     return true;
    102   }
    103 
    104   Close();
    105 
    106   file_util::CreateDirectory(new_path.DirName());
    107 
    108 #if defined(OS_WIN)
    109   // We cannot rename because rename will keep the same security descriptor
    110   // on the destination file. We want to recreate the security descriptor
    111   // with the security that makes sense in the new path.
    112   if (!file_util::RenameFileAndResetSecurityDescriptor(full_path_, new_path))
    113     return false;
    114 #elif defined(OS_POSIX)
    115   {
    116     // Similarly, on Unix, we're moving a temp file created with permissions
    117     // 600 to |new_path|. Here, we try to fix up the destination file with
    118     // appropriate permissions.
    119     struct stat st;
    120     // First check the file existence and create an empty file if it doesn't
    121     // exist.
    122     if (!file_util::PathExists(new_path))
    123       file_util::WriteFile(new_path, "", 0);
    124     bool stat_succeeded = (stat(new_path.value().c_str(), &st) == 0);
    125 
    126     // TODO(estade): Move() falls back to copying and deleting when a simple
    127     // rename fails. Copying sucks for large downloads. crbug.com/8737
    128     if (!file_util::Move(full_path_, new_path))
    129       return false;
    130 
    131     if (stat_succeeded)
    132       chmod(new_path.value().c_str(), st.st_mode);
    133   }
    134 #endif
    135 
    136   full_path_ = new_path;
    137 
    138   // We don't need to re-open the file if we're done (finished or canceled).
    139   if (!saved_in_progress)
    140     return true;
    141 
    142   if (!Open())
    143     return false;
    144 
    145   return true;
    146 }
    147 
    148 void BaseFile::Detach() {
    149   detached_ = true;
    150 }
    151 
    152 void BaseFile::Cancel() {
    153   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    154   DCHECK(!detached_);
    155 
    156   Close();
    157 
    158   if (!full_path_.empty())
    159     file_util::Delete(full_path_, false);
    160 }
    161 
    162 void BaseFile::Finish() {
    163   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    164 
    165   if (calculate_hash_)
    166     secure_hash_->Finish(sha256_hash_, kSha256HashLen);
    167 
    168   Close();
    169 }
    170 
    171 bool BaseFile::GetSha256Hash(std::string* hash) {
    172   DCHECK(!detached_);
    173   if (!calculate_hash_ || in_progress())
    174     return false;
    175   hash->assign(reinterpret_cast<const char*>(sha256_hash_),
    176                sizeof(sha256_hash_));
    177   return true;
    178 }
    179 
    180 void BaseFile::AnnotateWithSourceInformation() {
    181   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    182   DCHECK(!detached_);
    183 
    184 #if defined(OS_WIN)
    185   // Sets the Zone to tell Windows that this file comes from the internet.
    186   // We ignore the return value because a failure is not fatal.
    187   win_util::SetInternetZoneIdentifier(full_path_);
    188 #elif defined(OS_MACOSX)
    189   file_metadata::AddQuarantineMetadataToFile(full_path_, source_url_,
    190                                              referrer_url_);
    191   file_metadata::AddOriginMetadataToFile(full_path_, source_url_,
    192                                          referrer_url_);
    193 #endif
    194 }
    195 
    196 bool BaseFile::Open() {
    197   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    198   DCHECK(!detached_);
    199   DCHECK(!full_path_.empty());
    200 
    201   // Create a new file stream if it is not provided.
    202   if (!file_stream_.get()) {
    203     file_stream_.reset(new net::FileStream);
    204     if (file_stream_->Open(full_path_,
    205                            base::PLATFORM_FILE_OPEN_ALWAYS |
    206                                base::PLATFORM_FILE_WRITE) != net::OK) {
    207       file_stream_.reset();
    208       return false;
    209     }
    210 
    211     // We may be re-opening the file after rename. Always make sure we're
    212     // writing at the end of the file.
    213     if (file_stream_->Seek(net::FROM_END, 0) < 0) {
    214       file_stream_.reset();
    215       return false;
    216     }
    217   }
    218 
    219 #if defined(OS_WIN)
    220   AnnotateWithSourceInformation();
    221 #endif
    222   return true;
    223 }
    224 
    225 void BaseFile::Close() {
    226   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    227   if (file_stream_.get()) {
    228 #if defined(OS_CHROMEOS)
    229     // Currently we don't really care about the return value, since if it fails
    230     // theres not much we can do.  But we might in the future.
    231     file_stream_->Flush();
    232 #endif
    233     file_stream_->Close();
    234     file_stream_.reset();
    235   }
    236 }
    237 
    238 std::string BaseFile::DebugString() const {
    239   return base::StringPrintf("{ source_url_ = \"%s\""
    240                             " full_path_ = \"%" PRFilePath "\""
    241                             " bytes_so_far_ = %" PRId64 " detached_ = %c }",
    242                             source_url_.spec().c_str(),
    243                             full_path_.value().c_str(),
    244                             bytes_so_far_,
    245                             detached_ ? 'T' : 'F');
    246 }
    247