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

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

Issue 7646025: Detect file system errors during downloads. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merged with trunk. Created 9 years, 4 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 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/download_file_manager.h" 5 #include "content/browser/download/download_file_manager.h"
6 6
7 #include "base/file_util.h" 7 #include "base/file_util.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/stl_util.h" 9 #include "base/stl_util.h"
10 #include "base/task.h" 10 #include "base/task.h"
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 // DownloadFile has been deleted. 161 // DownloadFile has been deleted.
162 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { 162 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) {
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
164 std::vector<DownloadBuffer::Contents> contents; 164 std::vector<DownloadBuffer::Contents> contents;
165 { 165 {
166 base::AutoLock auto_lock(buffer->lock); 166 base::AutoLock auto_lock(buffer->lock);
167 contents.swap(buffer->contents); 167 contents.swap(buffer->contents);
168 } 168 }
169 169
170 DownloadFile* download_file = GetDownloadFile(id); 170 DownloadFile* download_file = GetDownloadFile(id);
171 bool had_error = false;
171 for (size_t i = 0; i < contents.size(); ++i) { 172 for (size_t i = 0; i < contents.size(); ++i) {
172 net::IOBuffer* data = contents[i].first; 173 net::IOBuffer* data = contents[i].first;
173 const int data_len = contents[i].second; 174 const int data_len = contents[i].second;
174 if (download_file) 175 if (!had_error && download_file) {
175 download_file->AppendDataToFile(data->data(), data_len); 176 bool write_succeeded =
177 download_file->AppendDataToFile(data->data(), data_len);
178 if (!write_succeeded) {
179 // Write failed: interrupt the download.
180 std::string hash;
cbentzel 2011/08/19 18:44:21 hash does not appear to be used.
ahendrickson 2011/08/22 14:53:30 Ah, leftover from a refactor. Removed.
181 DownloadManager* download_manager = download_file->GetDownloadManager();
182 had_error = true;
183
184 // Calling this here in case we get more data, to avoid calling
cbentzel 2011/08/19 18:44:21 Update this comment to discuss the issue with writ
Randy Smith (Not in Mondays) 2011/08/19 20:09:13 I'd suggest "this"->"CancelDownload" in the commen
ahendrickson 2011/08/22 14:53:30 Moved the comment.
ahendrickson 2011/08/22 14:53:30 Done.
185 // |OnDownloadError()| more than once.
186 int64 bytes_downloaded = download_file->bytes_so_far();
187 CancelDownload(id);
188
cbentzel 2011/08/19 18:44:21 Perhaps you should do a download_file = NULL; a
ahendrickson 2011/08/22 14:53:30 Done.
189 if (download_manager) {
190 BrowserThread::PostTask(
191 BrowserThread::UI,
192 FROM_HERE,
193 NewRunnableMethod(
194 download_manager,
195 &DownloadManager::OnDownloadError,
196 id,
197 bytes_downloaded,
198 net::ERR_FAILED));
199 }
200 }
201 }
176 data->Release(); 202 data->Release();
177 } 203 }
178 } 204 }
179 205
180 void DownloadFileManager::OnResponseCompleted( 206 void DownloadFileManager::OnResponseCompleted(
181 int id, 207 int id,
182 DownloadBuffer* buffer, 208 DownloadBuffer* buffer,
183 int os_error, 209 int net_error,
184 const std::string& security_info) { 210 const std::string& security_info) {
185 VLOG(20) << __FUNCTION__ << "()" << " id = " << id 211 VLOG(20) << __FUNCTION__ << "()" << " id = " << id
186 << " os_error = " << os_error 212 << " net_error = " << net_error
187 << " security_info = \"" << security_info << "\""; 213 << " security_info = \"" << security_info << "\"";
188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
189 delete buffer; 215 delete buffer;
190 DownloadFile* download_file = GetDownloadFile(id); 216 DownloadFile* download_file = GetDownloadFile(id);
191 if (!download_file) 217 if (!download_file)
192 return; 218 return;
193 219
194 download_file->Finish(); 220 download_file->Finish();
195 221
196 DownloadManager* download_manager = download_file->GetDownloadManager(); 222 DownloadManager* download_manager = download_file->GetDownloadManager();
197 if (!download_manager) { 223 if (!download_manager) {
198 CancelDownload(id); 224 CancelDownload(id);
199 return; 225 return;
200 } 226 }
201 227
202 std::string hash; 228 std::string hash;
203 if (!download_file->GetSha256Hash(&hash)) 229 if (!download_file->GetSha256Hash(&hash))
204 hash.clear(); 230 hash.clear();
205 231
206 BrowserThread::PostTask( 232 // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild
207 BrowserThread::UI, FROM_HERE, 233 // advertise a larger Content-Length than the amount of bytes in the message
208 NewRunnableMethod( 234 // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1,
209 download_manager, &DownloadManager::OnResponseCompleted, 235 // and Safari 5.0.4 - treat the download as complete in this case, so we
210 id, download_file->bytes_so_far(), os_error, hash)); 236 // follow their lead.
237 if (net_error == net::OK || net_error == net::ERR_CONNECTION_CLOSED) {
238 BrowserThread::PostTask(
239 BrowserThread::UI,
240 FROM_HERE,
241 NewRunnableMethod(
242 download_manager,
243 &DownloadManager::OnResponseCompleted,
244 id,
245 download_file->bytes_so_far(),
246 hash));
247 } else {
248 BrowserThread::PostTask(
249 BrowserThread::UI,
250 FROM_HERE,
251 NewRunnableMethod(
252 download_manager,
253 &DownloadManager::OnDownloadError,
254 id,
255 download_file->bytes_so_far(),
256 net_error));
257 }
211 // We need to keep the download around until the UI thread has finalized 258 // We need to keep the download around until the UI thread has finalized
212 // the name. 259 // the name.
213 } 260 }
214 261
215 // This method will be sent via a user action, or shutdown on the UI thread, and 262 // This method will be sent via a user action, or shutdown on the UI thread, and
216 // run on the download thread. Since this message has been sent from the UI 263 // run on the download thread. Since this message has been sent from the UI
217 // thread, the download may have already completed and won't exist in our map. 264 // thread, the download may have already completed and won't exist in our map.
218 void DownloadFileManager::CancelDownload(int id) { 265 void DownloadFileManager::CancelDownload(int id) {
219 VLOG(20) << __FUNCTION__ << "()" << " id = " << id; 266 VLOG(20) << __FUNCTION__ << "()" << " id = " << id;
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 DownloadFile* download_file = GetDownloadFile(id); 333 DownloadFile* download_file = GetDownloadFile(id);
287 if (!download_file) 334 if (!download_file)
288 return; 335 return;
289 336
290 VLOG(20) << __FUNCTION__ << "()" 337 VLOG(20) << __FUNCTION__ << "()"
291 << " download_file = " << download_file->DebugString(); 338 << " download_file = " << download_file->DebugString();
292 339
293 if (!download_file->Rename(full_path)) { 340 if (!download_file->Rename(full_path)) {
294 // Error. Between the time the UI thread generated 'full_path' to the time 341 // Error. Between the time the UI thread generated 'full_path' to the time
295 // this code runs, something happened that prevents us from renaming. 342 // this code runs, something happened that prevents us from renaming.
296 CancelDownloadOnRename(id); 343 CancelDownloadOnRename(id, net::ERR_FAILED);
297 } 344 }
298 } 345 }
299 346
300 // The DownloadManager in the UI thread has provided a final name for the 347 // The DownloadManager in the UI thread has provided a final name for the
301 // download specified by 'id'. Rename the download that's in the process 348 // download specified by 'id'. Rename the download that's in the process
302 // of completing. 349 // of completing.
303 // 350 //
304 // There are 2 possible rename cases where this method can be called: 351 // There are 2 possible rename cases where this method can be called:
305 // 1. foo.crdownload -> foo (final, safe) 352 // 1. foo.crdownload -> foo (final, safe)
306 // 2. Unconfirmed.xxx.crdownload -> xxx (final, validated) 353 // 2. Unconfirmed.xxx.crdownload -> xxx (final, validated)
(...skipping 27 matching lines...) Expand all
334 uniquifier = download_util::GetUniquePathNumber(new_path); 381 uniquifier = download_util::GetUniquePathNumber(new_path);
335 if (uniquifier > 0) { 382 if (uniquifier > 0) {
336 download_util::AppendNumberToPath(&new_path, uniquifier); 383 download_util::AppendNumberToPath(&new_path, uniquifier);
337 } 384 }
338 } 385 }
339 386
340 // Rename the file, overwriting if necessary. 387 // Rename the file, overwriting if necessary.
341 if (!download_file->Rename(new_path)) { 388 if (!download_file->Rename(new_path)) {
342 // Error. Between the time the UI thread generated 'full_path' to the time 389 // Error. Between the time the UI thread generated 'full_path' to the time
343 // this code runs, something happened that prevents us from renaming. 390 // this code runs, something happened that prevents us from renaming.
344 CancelDownloadOnRename(id); 391 CancelDownloadOnRename(id, net::ERR_FAILED);
345 return; 392 return;
346 } 393 }
347 394
348 #if defined(OS_MACOSX) 395 #if defined(OS_MACOSX)
349 // Done here because we only want to do this once; see 396 // Done here because we only want to do this once; see
350 // http://crbug.com/13120 for details. 397 // http://crbug.com/13120 for details.
351 download_file->AnnotateWithSourceInformation(); 398 download_file->AnnotateWithSourceInformation();
352 #endif 399 #endif
353 400
354 BrowserThread::PostTask( 401 BrowserThread::PostTask(
355 BrowserThread::UI, FROM_HERE, 402 BrowserThread::UI, FROM_HERE,
356 NewRunnableMethod( 403 NewRunnableMethod(
357 download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id, 404 download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id,
358 new_path, uniquifier)); 405 new_path, uniquifier));
359 } 406 }
360 407
361 // Called only from RenameInProgressDownloadFile and 408 // Called only from RenameInProgressDownloadFile and
362 // RenameCompletingDownloadFile on the FILE thread. 409 // RenameCompletingDownloadFile on the FILE thread.
363 void DownloadFileManager::CancelDownloadOnRename(int id) { 410 void DownloadFileManager::CancelDownloadOnRename(int id, int rename_error) {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
365 412
366 DownloadFile* download_file = GetDownloadFile(id); 413 DownloadFile* download_file = GetDownloadFile(id);
367 if (!download_file) 414 if (!download_file)
368 return; 415 return;
369 416
370 DownloadManager* download_manager = download_file->GetDownloadManager(); 417 DownloadManager* download_manager = download_file->GetDownloadManager();
371 if (!download_manager) { 418 if (!download_manager) {
372 // Without a download manager, we can't cancel the request normally, so we 419 // Without a download manager, we can't cancel the request normally, so we
373 // need to do it here. The normal path will also update the download 420 // need to do it here. The normal path will also update the download
374 // history before cancelling the request. 421 // history before canceling the request.
375 download_file->CancelDownloadRequest(); 422 download_file->CancelDownloadRequest();
376 return; 423 return;
377 } 424 }
378 425
379 BrowserThread::PostTask( 426 BrowserThread::PostTask(
380 BrowserThread::UI, FROM_HERE, 427 BrowserThread::UI, FROM_HERE,
381 NewRunnableMethod(download_manager, 428 NewRunnableMethod(download_manager,
382 &DownloadManager::DownloadCancelled, id)); 429 &DownloadManager::OnDownloadError,
430 id,
431 download_file->bytes_so_far(),
432 rename_error));
383 } 433 }
384 434
385 void DownloadFileManager::EraseDownload(int id) { 435 void DownloadFileManager::EraseDownload(int id) {
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
387 437
388 if (!ContainsKey(downloads_, id)) 438 if (!ContainsKey(downloads_, id))
389 return; 439 return;
390 440
391 DownloadFile* download_file = downloads_[id]; 441 DownloadFile* download_file = downloads_[id];
392 442
393 VLOG(20) << " " << __FUNCTION__ << "()" 443 VLOG(20) << " " << __FUNCTION__ << "()"
394 << " id = " << id 444 << " id = " << id
395 << " download_file = " << download_file->DebugString(); 445 << " download_file = " << download_file->DebugString();
396 446
397 downloads_.erase(id); 447 downloads_.erase(id);
398 448
399 delete download_file; 449 delete download_file;
400 450
401 if (downloads_.empty()) 451 if (downloads_.empty())
402 StopUpdateTimer(); 452 StopUpdateTimer();
403 } 453 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698