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