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 |