Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/download/base_file.h" | 5 #include "content/browser/download/base_file.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/files/file.h" | 10 #include "base/files/file.h" |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 35 if (detached_) | 35 if (detached_) |
| 36 Close(); | 36 Close(); |
| 37 else | 37 else |
| 38 Cancel(); // Will delete the file. | 38 Cancel(); // Will delete the file. |
| 39 } | 39 } |
| 40 | 40 |
| 41 DownloadInterruptReason BaseFile::Initialize( | 41 DownloadInterruptReason BaseFile::Initialize( |
| 42 const base::FilePath& full_path, | 42 const base::FilePath& full_path, |
| 43 const base::FilePath& default_directory, | 43 const base::FilePath& default_directory, |
| 44 base::File file, | 44 base::File file, |
| 45 int64_t bytes_so_far, | 45 int64_t offset, |
| 46 const std::string& hash_so_far, | 46 const std::string& hash_so_far, |
| 47 std::unique_ptr<crypto::SecureHash> hash_state) { | 47 std::unique_ptr<crypto::SecureHash> hash_state, |
| 48 AccessMode access_mode) { | |
| 48 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 49 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 49 DCHECK(!detached_); | 50 DCHECK(!detached_); |
| 50 | 51 |
| 51 if (full_path.empty()) { | 52 if (full_path.empty()) { |
| 52 base::FilePath initial_directory(default_directory); | 53 base::FilePath initial_directory(default_directory); |
| 53 base::FilePath temp_file; | 54 base::FilePath temp_file; |
| 54 if (initial_directory.empty()) { | 55 if (initial_directory.empty()) { |
| 55 initial_directory = | 56 initial_directory = |
| 56 GetContentClient()->browser()->GetDefaultDownloadDirectory(); | 57 GetContentClient()->browser()->GetDefaultDownloadDirectory(); |
| 57 } | 58 } |
| 58 // |initial_directory| can still be empty if ContentBrowserClient returned | 59 // |initial_directory| can still be empty if ContentBrowserClient returned |
| 59 // an empty path for the downloads directory. | 60 // an empty path for the downloads directory. |
| 60 if ((initial_directory.empty() || | 61 if ((initial_directory.empty() || |
| 61 !base::CreateTemporaryFileInDir(initial_directory, &temp_file)) && | 62 !base::CreateTemporaryFileInDir(initial_directory, &temp_file)) && |
| 62 !base::CreateTemporaryFile(&temp_file)) { | 63 !base::CreateTemporaryFile(&temp_file)) { |
| 63 return LogInterruptReason("Unable to create", 0, | 64 return LogInterruptReason("Unable to create", 0, |
| 64 DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); | 65 DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
| 65 } | 66 } |
| 66 full_path_ = temp_file; | 67 full_path_ = temp_file; |
| 67 } else { | 68 } else { |
| 68 full_path_ = full_path; | 69 full_path_ = full_path; |
| 69 } | 70 } |
| 70 | 71 |
| 71 bytes_so_far_ = bytes_so_far; | 72 access_mode_ = access_mode; |
| 73 offset_ = offset; | |
| 74 if (access_mode_ == EXCLUSIVE) | |
| 75 bytes_so_far_ = offset; | |
| 72 secure_hash_ = std::move(hash_state); | 76 secure_hash_ = std::move(hash_state); |
| 73 file_ = std::move(file); | 77 file_ = std::move(file); |
| 74 | 78 |
| 75 return Open(hash_so_far); | 79 return Open(hash_so_far); |
| 76 } | 80 } |
| 77 | 81 |
| 78 DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, | 82 DownloadInterruptReason BaseFile::WriteDataToFile(const char* data, |
| 79 size_t data_len) { | 83 size_t data_len) { |
| 80 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 84 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 81 DCHECK(!detached_); | 85 DCHECK(!detached_); |
| 82 | 86 |
| 83 // NOTE(benwells): The above DCHECK won't be present in release builds, | 87 // NOTE(benwells): The above DCHECK won't be present in release builds, |
| 84 // so we log any occurences to see how common this error is in the wild. | 88 // so we log any occurences to see how common this error is in the wild. |
| 85 if (detached_) | 89 if (detached_) |
| 86 RecordDownloadCount(APPEND_TO_DETACHED_FILE_COUNT); | 90 RecordDownloadCount(APPEND_TO_DETACHED_FILE_COUNT); |
| 87 | 91 |
| 88 if (!file_.IsValid()) | 92 if (!file_.IsValid()) |
| 89 return LogInterruptReason("No file stream on append", 0, | 93 return LogInterruptReason("No file stream on append", 0, |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 105 | 109 |
| 106 // Report errors on file writes. | 110 // Report errors on file writes. |
| 107 if (write_result < 0) | 111 if (write_result < 0) |
| 108 return LogSystemError("Write", logging::GetLastSystemErrorCode()); | 112 return LogSystemError("Write", logging::GetLastSystemErrorCode()); |
| 109 | 113 |
| 110 // Update status. | 114 // Update status. |
| 111 size_t write_size = static_cast<size_t>(write_result); | 115 size_t write_size = static_cast<size_t>(write_result); |
| 112 DCHECK_LE(write_size, len); | 116 DCHECK_LE(write_size, len); |
| 113 len -= write_size; | 117 len -= write_size; |
| 114 current_data += write_size; | 118 current_data += write_size; |
| 119 offset_ += write_size; | |
| 115 bytes_so_far_ += write_size; | 120 bytes_so_far_ += write_size; |
| 116 } | 121 } |
| 117 net_log_.EndEvent(net::NetLogEventType::DOWNLOAD_FILE_WRITTEN, | 122 net_log_.EndEvent(net::NetLogEventType::DOWNLOAD_FILE_WRITTEN, |
| 118 net::NetLog::Int64Callback("bytes", data_len)); | 123 net::NetLog::Int64Callback("bytes", data_len)); |
| 119 | 124 |
| 120 if (secure_hash_) | 125 if (secure_hash_) |
|
xingliu
2017/02/15 19:13:20
Maybe add DCHECK(access_mode_ == AccessMode::EXCLU
qinmin
2017/02/15 20:00:43
secure_hash_ is non null, so this should be in the
xingliu
2017/02/15 20:43:57
Got it, sorry I thought in EXCLUSIVE mode there wi
| |
| 121 secure_hash_->Update(data, data_len); | 126 secure_hash_->Update(data, data_len); |
| 122 | 127 |
| 123 return DOWNLOAD_INTERRUPT_REASON_NONE; | 128 return DOWNLOAD_INTERRUPT_REASON_NONE; |
| 124 } | 129 } |
| 125 | 130 |
| 126 DownloadInterruptReason BaseFile::Rename(const base::FilePath& new_path) { | 131 DownloadInterruptReason BaseFile::Rename(const base::FilePath& new_path) { |
| 127 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 132 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 128 DownloadInterruptReason rename_result = DOWNLOAD_INTERRUPT_REASON_NONE; | 133 DownloadInterruptReason rename_result = DOWNLOAD_INTERRUPT_REASON_NONE; |
| 129 | 134 |
| 130 // If the new path is same as the old one, there is no need to perform the | 135 // If the new path is same as the old one, there is no need to perform the |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 188 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 193 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 189 Close(); | 194 Close(); |
| 190 return std::move(secure_hash_); | 195 return std::move(secure_hash_); |
| 191 } | 196 } |
| 192 | 197 |
| 193 std::string BaseFile::DebugString() const { | 198 std::string BaseFile::DebugString() const { |
| 194 return base::StringPrintf( | 199 return base::StringPrintf( |
| 195 "{ " | 200 "{ " |
| 196 " full_path_ = \"%" PRFilePath | 201 " full_path_ = \"%" PRFilePath |
| 197 "\"" | 202 "\"" |
| 198 " bytes_so_far_ = %" PRId64 " detached_ = %c }", | 203 " offset_ = %" PRId64 " detached_ = %c }", |
| 199 full_path_.value().c_str(), | 204 full_path_.value().c_str(), offset_, detached_ ? 'T' : 'F'); |
| 200 bytes_so_far_, | |
| 201 detached_ ? 'T' : 'F'); | |
| 202 } | 205 } |
| 203 | 206 |
| 204 DownloadInterruptReason BaseFile::CalculatePartialHash( | 207 DownloadInterruptReason BaseFile::CalculatePartialHash( |
| 205 const std::string& hash_to_expect) { | 208 const std::string& hash_to_expect) { |
| 206 secure_hash_ = crypto::SecureHash::Create(crypto::SecureHash::SHA256); | 209 secure_hash_ = crypto::SecureHash::Create(crypto::SecureHash::SHA256); |
| 207 | 210 |
| 208 if (bytes_so_far_ == 0) | 211 if (access_mode_ == SHARED) |
| 212 return DOWNLOAD_INTERRUPT_REASON_NONE; | |
| 213 | |
| 214 if (offset_ == 0) | |
| 209 return DOWNLOAD_INTERRUPT_REASON_NONE; | 215 return DOWNLOAD_INTERRUPT_REASON_NONE; |
| 210 | 216 |
| 211 if (file_.Seek(base::File::FROM_BEGIN, 0) != 0) | 217 if (file_.Seek(base::File::FROM_BEGIN, 0) != 0) |
| 212 return LogSystemError("Seek partial file", | 218 return LogSystemError("Seek partial file", |
| 213 logging::GetLastSystemErrorCode()); | 219 logging::GetLastSystemErrorCode()); |
| 214 | 220 |
| 215 const size_t kMinBufferSize = secure_hash_->GetHashLength(); | 221 const size_t kMinBufferSize = secure_hash_->GetHashLength(); |
| 216 const size_t kMaxBufferSize = 1024 * 512; | 222 const size_t kMaxBufferSize = 1024 * 512; |
| 217 static_assert(kMaxBufferSize <= std::numeric_limits<int>::max(), | 223 static_assert(kMaxBufferSize <= std::numeric_limits<int>::max(), |
| 218 "kMaxBufferSize must fit on an int"); | 224 "kMaxBufferSize must fit on an int"); |
| 219 | 225 |
| 220 // The size of the buffer is: | 226 // The size of the buffer is: |
| 221 // - at least kMinBufferSize so that we can use it to hold the hash as well. | 227 // - at least kMinBufferSize so that we can use it to hold the hash as well. |
| 222 // - at most kMaxBufferSize so that there's a reasonable bound. | 228 // - at most kMaxBufferSize so that there's a reasonable bound. |
| 223 // - not larger than |bytes_so_far_| unless bytes_so_far_ is less than the | 229 // - not larger than |offset_| unless |offset_| is less than the hash size. |
| 224 // hash size. | |
| 225 std::vector<char> buffer(std::max<int64_t>( | 230 std::vector<char> buffer(std::max<int64_t>( |
| 226 kMinBufferSize, std::min<int64_t>(kMaxBufferSize, bytes_so_far_))); | 231 kMinBufferSize, std::min<int64_t>(kMaxBufferSize, offset_))); |
| 227 | 232 |
| 228 int64_t current_position = 0; | 233 int64_t current_position = 0; |
| 229 while (current_position < bytes_so_far_) { | 234 while (current_position < offset_) { |
| 230 // While std::min needs to work with int64_t, the result is always at most | 235 // While std::min needs to work with int64_t, the result is always at most |
| 231 // kMaxBufferSize, which fits on an int. | 236 // kMaxBufferSize, which fits on an int. |
| 232 int bytes_to_read = | 237 int bytes_to_read = |
| 233 std::min<int64_t>(buffer.size(), bytes_so_far_ - current_position); | 238 std::min<int64_t>(buffer.size(), offset_ - current_position); |
| 234 int length = file_.ReadAtCurrentPos(&buffer.front(), bytes_to_read); | 239 int length = file_.ReadAtCurrentPos(&buffer.front(), bytes_to_read); |
| 235 if (length == -1) { | 240 if (length == -1) { |
| 236 return LogInterruptReason("Reading partial file", | 241 return LogInterruptReason("Reading partial file", |
| 237 logging::GetLastSystemErrorCode(), | 242 logging::GetLastSystemErrorCode(), |
| 238 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); | 243 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); |
| 239 } | 244 } |
| 240 | 245 |
| 241 if (length == 0) | 246 if (length == 0) |
| 242 break; | 247 break; |
| 243 | 248 |
| 244 secure_hash_->Update(&buffer.front(), length); | 249 secure_hash_->Update(&buffer.front(), length); |
| 245 current_position += length; | 250 current_position += length; |
| 246 } | 251 } |
| 247 | 252 |
| 248 if (current_position != bytes_so_far_) { | 253 if (current_position != offset_) { |
| 249 return LogInterruptReason( | 254 return LogInterruptReason( |
| 250 "Verifying prefix hash", 0, DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); | 255 "Verifying prefix hash", 0, DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); |
| 251 } | 256 } |
| 252 | 257 |
| 253 if (!hash_to_expect.empty()) { | 258 if (!hash_to_expect.empty()) { |
| 254 DCHECK_EQ(secure_hash_->GetHashLength(), hash_to_expect.size()); | 259 DCHECK_EQ(secure_hash_->GetHashLength(), hash_to_expect.size()); |
| 255 DCHECK(buffer.size() >= secure_hash_->GetHashLength()); | 260 DCHECK(buffer.size() >= secure_hash_->GetHashLength()); |
| 256 std::unique_ptr<crypto::SecureHash> partial_hash(secure_hash_->Clone()); | 261 std::unique_ptr<crypto::SecureHash> partial_hash(secure_hash_->Clone()); |
| 257 partial_hash->Finish(&buffer.front(), buffer.size()); | 262 partial_hash->Finish(&buffer.front(), buffer.size()); |
| 258 | 263 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 279 base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE | | 284 base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE | |
| 280 base::File::FLAG_READ); | 285 base::File::FLAG_READ); |
| 281 if (!file_.IsValid()) { | 286 if (!file_.IsValid()) { |
| 282 return LogNetError("Open/Initialize File", | 287 return LogNetError("Open/Initialize File", |
| 283 net::FileErrorToNetError(file_.error_details())); | 288 net::FileErrorToNetError(file_.error_details())); |
| 284 } | 289 } |
| 285 } | 290 } |
| 286 | 291 |
| 287 net_log_.BeginEvent( | 292 net_log_.BeginEvent( |
| 288 net::NetLogEventType::DOWNLOAD_FILE_OPENED, | 293 net::NetLogEventType::DOWNLOAD_FILE_OPENED, |
| 289 base::Bind(&FileOpenedNetLogCallback, &full_path_, bytes_so_far_)); | 294 base::Bind(&FileOpenedNetLogCallback, &full_path_, offset_)); |
| 295 | |
| 296 if (access_mode_ == SHARED) { | |
| 297 if (file_.Seek(base::File::FROM_BEGIN, offset_) < 0) { | |
| 298 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); | |
| 299 ClearFile(); | |
| 300 return LogSystemError("Seeking to end", error); | |
| 301 } | |
| 302 return DOWNLOAD_INTERRUPT_REASON_NONE; | |
| 303 } | |
| 290 | 304 |
| 291 if (!secure_hash_) { | 305 if (!secure_hash_) { |
| 292 DownloadInterruptReason reason = CalculatePartialHash(hash_so_far); | 306 DownloadInterruptReason reason = CalculatePartialHash(hash_so_far); |
| 293 if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) { | 307 if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) { |
| 294 ClearFile(); | 308 ClearFile(); |
| 295 return reason; | 309 return reason; |
| 296 } | 310 } |
| 297 } | 311 } |
| 298 | 312 |
| 299 int64_t file_size = file_.Seek(base::File::FROM_END, 0); | 313 int64_t file_size = file_.Seek(base::File::FROM_END, 0); |
| 300 if (file_size < 0) { | 314 if (file_size < 0) { |
| 301 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); | 315 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); |
| 302 ClearFile(); | 316 ClearFile(); |
| 303 return LogSystemError("Seeking to end", error); | 317 return LogSystemError("Seeking to end", error); |
| 304 } else if (file_size > bytes_so_far_) { | 318 } else if (file_size > offset_) { |
| 305 // The file is larger than we expected. | 319 // The file is larger than we expected. |
| 306 // This is OK, as long as we don't use the extra. | 320 // This is OK, as long as we don't use the extra. |
| 307 // Truncate the file. | 321 // Truncate the file. |
| 308 if (!file_.SetLength(bytes_so_far_) || | 322 if (!file_.SetLength(offset_) || |
| 309 file_.Seek(base::File::FROM_BEGIN, bytes_so_far_) != bytes_so_far_) { | 323 file_.Seek(base::File::FROM_BEGIN, offset_) != offset_) { |
| 310 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); | 324 logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); |
| 311 ClearFile(); | 325 ClearFile(); |
| 312 return LogSystemError("Truncating to last known offset", error); | 326 return LogSystemError("Truncating to last known offset", error); |
| 313 } | 327 } |
| 314 } else if (file_size < bytes_so_far_) { | 328 } else if (file_size < offset_) { |
| 315 // The file is shorter than we expected. Our hashes won't be valid. | 329 // The file is shorter than we expected. Our hashes won't be valid. |
| 316 ClearFile(); | 330 ClearFile(); |
| 317 return LogInterruptReason("Unable to seek to last written point", 0, | 331 return LogInterruptReason("Unable to seek to last written point", 0, |
| 318 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); | 332 DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); |
| 319 } | 333 } |
| 320 | 334 |
| 321 return DOWNLOAD_INTERRUPT_REASON_NONE; | 335 return DOWNLOAD_INTERRUPT_REASON_NONE; |
| 322 } | 336 } |
| 323 | 337 |
| 324 void BaseFile::Close() { | 338 void BaseFile::Close() { |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 456 #else // !OS_WIN && !OS_MACOSX && !OS_LINUX | 470 #else // !OS_WIN && !OS_MACOSX && !OS_LINUX |
| 457 DownloadInterruptReason BaseFile::AnnotateWithSourceInformation( | 471 DownloadInterruptReason BaseFile::AnnotateWithSourceInformation( |
| 458 const std::string& client_guid, | 472 const std::string& client_guid, |
| 459 const GURL& source_url, | 473 const GURL& source_url, |
| 460 const GURL& referrer_url) { | 474 const GURL& referrer_url) { |
| 461 return DOWNLOAD_INTERRUPT_REASON_NONE; | 475 return DOWNLOAD_INTERRUPT_REASON_NONE; |
| 462 } | 476 } |
| 463 #endif | 477 #endif |
| 464 | 478 |
| 465 } // namespace content | 479 } // namespace content |
| OLD | NEW |