Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Side by Side Diff: content/browser/download/base_file.cc

Issue 2695153002: Refactor BaseFile class to support sparse files (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698