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