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

Side by Side Diff: chrome/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: 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 "chrome/browser/download/download_file_manager.h" 5 #include "chrome/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 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 // DownloadFile has been deleted. 162 // DownloadFile has been deleted.
163 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { 163 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) {
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
165 std::vector<DownloadBuffer::Contents> contents; 165 std::vector<DownloadBuffer::Contents> contents;
166 { 166 {
167 base::AutoLock auto_lock(buffer->lock); 167 base::AutoLock auto_lock(buffer->lock);
168 contents.swap(buffer->contents); 168 contents.swap(buffer->contents);
169 } 169 }
170 170
171 DownloadFile* download_file = GetDownloadFile(id); 171 DownloadFile* download_file = GetDownloadFile(id);
172 bool had_error = false;
172 for (size_t i = 0; i < contents.size(); ++i) { 173 for (size_t i = 0; i < contents.size(); ++i) {
173 net::IOBuffer* data = contents[i].first; 174 net::IOBuffer* data = contents[i].first;
174 const int data_len = contents[i].second; 175 const int data_len = contents[i].second;
175 if (download_file) 176 if (!had_error && download_file) {
176 download_file->AppendDataToFile(data->data(), data_len); 177 bool write_succeeded =
cbentzel 2011/08/16 15:50:36 If this is going to end up with a net_error code r
ahendrickson 2011/08/18 21:52:01 AppendDataToFile() returns a bool currently. Anot
178 download_file->AppendDataToFile(data->data(), data_len);
179 if (!write_succeeded) {
180 // Write failed: interrupt the download.
181 std::string hash;
182 DownloadManager* download_manager = download_file->GetDownloadManager();
183 had_error = true;
184
185 // Calling this here in case we get more data, to avoid calling
186 // |OnDownloadError()| more than once.
187 CancelDownload(id);
cbentzel 2011/08/16 15:50:36 BUG: I'm not sure the CancelDownload is right. a]
ahendrickson 2011/08/18 21:52:01 Fixed. Now get the number of bytes before canceli
cbentzel 2011/08/19 13:02:41 What race condition? I agree that calling CancelD
ahendrickson 2011/08/19 18:11:59 Hmm, I misspoke a bit, I think. We might not make
188
189 if (download_manager) {
190 BrowserThread::PostTask(
191 BrowserThread::UI,
192 FROM_HERE,
193 NewRunnableMethod(
194 download_manager,
195 &DownloadManager::OnDownloadError,
196 id,
197 download_file->bytes_so_far(),
198 net::ERR_FAILED));
199 }
200 }
201 }
177 data->Release(); 202 data->Release();
178 } 203 }
179 } 204 }
180 205
181 void DownloadFileManager::OnResponseCompleted( 206 void DownloadFileManager::OnResponseCompleted(
182 int id, 207 int id,
183 DownloadBuffer* buffer, 208 DownloadBuffer* buffer,
184 int os_error, 209 int net_error,
185 const std::string& security_info) { 210 const std::string& security_info) {
186 VLOG(20) << __FUNCTION__ << "()" << " id = " << id 211 VLOG(20) << __FUNCTION__ << "()" << " id = " << id
187 << " os_error = " << os_error 212 << " net_error = " << net_error
188 << " security_info = \"" << security_info << "\""; 213 << " security_info = \"" << security_info << "\"";
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
190 delete buffer; 215 delete buffer;
191 DownloadFile* download_file = GetDownloadFile(id); 216 DownloadFile* download_file = GetDownloadFile(id);
192 if (!download_file) 217 if (!download_file)
193 return; 218 return;
194 219
195 download_file->Finish(); 220 download_file->Finish();
196 221
197 DownloadManager* download_manager = download_file->GetDownloadManager(); 222 DownloadManager* download_manager = download_file->GetDownloadManager();
198 if (!download_manager) { 223 if (!download_manager) {
199 CancelDownload(id); 224 CancelDownload(id);
200 return; 225 return;
201 } 226 }
202 227
203 std::string hash; 228 std::string hash;
204 if (!download_file->GetSha256Hash(&hash)) 229 if (!download_file->GetSha256Hash(&hash))
205 hash.clear(); 230 hash.clear();
206 231
207 BrowserThread::PostTask( 232 // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild
208 BrowserThread::UI, FROM_HERE, 233 // advertise a larger Content-Length than the amount of bytes in the message
209 NewRunnableMethod( 234 // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1,
210 download_manager, &DownloadManager::OnResponseCompleted, 235 // and Safari 5.0.4 - treat the download as complete in this case, so we
211 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 }
212 // 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
213 // the name. 259 // the name.
214 } 260 }
215 261
216 // 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
217 // 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
218 // 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.
219 void DownloadFileManager::CancelDownload(int id) { 265 void DownloadFileManager::CancelDownload(int id) {
220 VLOG(20) << __FUNCTION__ << "()" << " id = " << id; 266 VLOG(20) << __FUNCTION__ << "()" << " id = " << id;
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
287 DownloadFile* download_file = GetDownloadFile(id); 333 DownloadFile* download_file = GetDownloadFile(id);
288 if (!download_file) 334 if (!download_file)
289 return; 335 return;
290 336
291 VLOG(20) << __FUNCTION__ << "()" 337 VLOG(20) << __FUNCTION__ << "()"
292 << " download_file = " << download_file->DebugString(); 338 << " download_file = " << download_file->DebugString();
293 339
294 if (!download_file->Rename(full_path)) { 340 if (!download_file->Rename(full_path)) {
295 // 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
296 // this code runs, something happened that prevents us from renaming. 342 // this code runs, something happened that prevents us from renaming.
297 CancelDownloadOnRename(id); 343 CancelDownloadOnRename(id, net::ERR_FAILED);
298 } 344 }
299 } 345 }
300 346
301 // 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
302 // 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
303 // of completing. 349 // of completing.
304 // 350 //
305 // 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:
306 // 1. foo.crdownload -> foo (final, safe) 352 // 1. foo.crdownload -> foo (final, safe)
307 // 2. Unconfirmed.xxx.crdownload -> xxx (final, validated) 353 // 2. Unconfirmed.xxx.crdownload -> xxx (final, validated)
(...skipping 27 matching lines...) Expand all
335 uniquifier = download_util::GetUniquePathNumber(new_path); 381 uniquifier = download_util::GetUniquePathNumber(new_path);
336 if (uniquifier > 0) { 382 if (uniquifier > 0) {
337 download_util::AppendNumberToPath(&new_path, uniquifier); 383 download_util::AppendNumberToPath(&new_path, uniquifier);
338 } 384 }
339 } 385 }
340 386
341 // Rename the file, overwriting if necessary. 387 // Rename the file, overwriting if necessary.
342 if (!download_file->Rename(new_path)) { 388 if (!download_file->Rename(new_path)) {
343 // 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
344 // this code runs, something happened that prevents us from renaming. 390 // this code runs, something happened that prevents us from renaming.
345 CancelDownloadOnRename(id); 391 CancelDownloadOnRename(id, net::ERR_FAILED);
346 return; 392 return;
347 } 393 }
348 394
349 #if defined(OS_MACOSX) 395 #if defined(OS_MACOSX)
350 // Done here because we only want to do this once; see 396 // Done here because we only want to do this once; see
351 // http://crbug.com/13120 for details. 397 // http://crbug.com/13120 for details.
352 download_file->AnnotateWithSourceInformation(); 398 download_file->AnnotateWithSourceInformation();
353 #endif 399 #endif
354 400
355 BrowserThread::PostTask( 401 BrowserThread::PostTask(
356 BrowserThread::UI, FROM_HERE, 402 BrowserThread::UI, FROM_HERE,
357 NewRunnableMethod( 403 NewRunnableMethod(
358 download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id, 404 download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id,
359 new_path, uniquifier)); 405 new_path, uniquifier));
360 } 406 }
361 407
362 // Called only from RenameInProgressDownloadFile and 408 // Called only from RenameInProgressDownloadFile and
363 // RenameCompletingDownloadFile on the FILE thread. 409 // RenameCompletingDownloadFile on the FILE thread.
364 void DownloadFileManager::CancelDownloadOnRename(int id) { 410 void DownloadFileManager::CancelDownloadOnRename(int id, int rename_error) {
365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
366 412
367 DownloadFile* download_file = GetDownloadFile(id); 413 DownloadFile* download_file = GetDownloadFile(id);
368 if (!download_file) 414 if (!download_file)
369 return; 415 return;
370 416
371 DownloadManager* download_manager = download_file->GetDownloadManager(); 417 DownloadManager* download_manager = download_file->GetDownloadManager();
372 if (!download_manager) { 418 if (!download_manager) {
373 // 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
374 // 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
375 // history before cancelling the request. 421 // history before canceling the request.
376 download_file->CancelDownloadRequest(); 422 download_file->CancelDownloadRequest();
377 return; 423 return;
378 } 424 }
379 425
380 BrowserThread::PostTask( 426 BrowserThread::PostTask(
381 BrowserThread::UI, FROM_HERE, 427 BrowserThread::UI, FROM_HERE,
382 NewRunnableMethod(download_manager, 428 NewRunnableMethod(download_manager,
383 &DownloadManager::DownloadCancelled, id)); 429 &DownloadManager::OnDownloadError,
430 id,
431 download_file->bytes_so_far(),
432 rename_error));
384 } 433 }
385 434
386 void DownloadFileManager::EraseDownload(int id) { 435 void DownloadFileManager::EraseDownload(int id) {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
388 437
389 if (!ContainsKey(downloads_, id)) 438 if (!ContainsKey(downloads_, id))
390 return; 439 return;
391 440
392 DownloadFile* download_file = downloads_[id]; 441 DownloadFile* download_file = downloads_[id];
393 442
394 VLOG(20) << " " << __FUNCTION__ << "()" 443 VLOG(20) << " " << __FUNCTION__ << "()"
395 << " id = " << id 444 << " id = " << id
396 << " download_file = " << download_file->DebugString(); 445 << " download_file = " << download_file->DebugString();
397 446
398 downloads_.erase(id); 447 downloads_.erase(id);
399 448
400 delete download_file; 449 delete download_file;
401 450
402 if (downloads_.empty()) 451 if (downloads_.empty())
403 StopUpdateTimer(); 452 StopUpdateTimer();
404 } 453 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698