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

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: Minor refactor of DownloadFileWithMockStream class. Created 9 years, 3 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 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
146 // DownloadFile has been deleted. 146 // DownloadFile has been deleted.
147 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { 147 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
149 std::vector<DownloadBuffer::Contents> contents; 149 std::vector<DownloadBuffer::Contents> contents;
150 { 150 {
151 base::AutoLock auto_lock(buffer->lock); 151 base::AutoLock auto_lock(buffer->lock);
152 contents.swap(buffer->contents); 152 contents.swap(buffer->contents);
153 } 153 }
154 154
155 DownloadFile* download_file = GetDownloadFile(id); 155 DownloadFile* download_file = GetDownloadFile(id);
156 bool had_error = false;
156 for (size_t i = 0; i < contents.size(); ++i) { 157 for (size_t i = 0; i < contents.size(); ++i) {
157 net::IOBuffer* data = contents[i].first; 158 net::IOBuffer* data = contents[i].first;
158 const int data_len = contents[i].second; 159 const int data_len = contents[i].second;
159 if (download_file) 160 if (!had_error && download_file) {
160 download_file->AppendDataToFile(data->data(), data_len); 161 bool write_succeeded =
162 download_file->AppendDataToFile(data->data(), data_len);
163 if (!write_succeeded) {
164 // Write failed: interrupt the download.
165 DownloadManager* download_manager = download_file->GetDownloadManager();
166 had_error = true;
167
168 int64 bytes_downloaded = download_file->bytes_so_far();
169 // Calling this here in case we get more data, to avoid
170 // processing data after an error. That could lead to
171 // files that are corrupted if the later processing succeeded.
172 CancelDownload(id);
173 download_file = NULL; // Was deleted in |CancelDownload|.
174
175 if (download_manager) {
176 BrowserThread::PostTask(
177 BrowserThread::UI,
178 FROM_HERE,
179 NewRunnableMethod(
180 download_manager,
181 &DownloadManager::OnDownloadError,
182 id,
183 bytes_downloaded,
184 net::ERR_FAILED));
185 }
186 }
187 }
161 data->Release(); 188 data->Release();
162 } 189 }
163 } 190 }
164 191
165 void DownloadFileManager::OnResponseCompleted( 192 void DownloadFileManager::OnResponseCompleted(
166 int id, 193 int id,
167 DownloadBuffer* buffer, 194 DownloadBuffer* buffer,
168 int os_error, 195 int net_error,
169 const std::string& security_info) { 196 const std::string& security_info) {
170 VLOG(20) << __FUNCTION__ << "()" << " id = " << id 197 VLOG(20) << __FUNCTION__ << "()" << " id = " << id
171 << " os_error = " << os_error 198 << " net_error = " << net_error
172 << " security_info = \"" << security_info << "\""; 199 << " security_info = \"" << security_info << "\"";
173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
174 delete buffer; 201 delete buffer;
175 DownloadFile* download_file = GetDownloadFile(id); 202 DownloadFile* download_file = GetDownloadFile(id);
176 if (!download_file) 203 if (!download_file)
177 return; 204 return;
178 205
179 download_file->Finish(); 206 download_file->Finish();
180 207
181 DownloadManager* download_manager = download_file->GetDownloadManager(); 208 DownloadManager* download_manager = download_file->GetDownloadManager();
182 if (!download_manager) { 209 if (!download_manager) {
183 CancelDownload(id); 210 CancelDownload(id);
184 return; 211 return;
185 } 212 }
186 213
187 std::string hash; 214 std::string hash;
188 if (!download_file->GetSha256Hash(&hash)) 215 if (!download_file->GetSha256Hash(&hash))
189 hash.clear(); 216 hash.clear();
190 217
191 BrowserThread::PostTask( 218 // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild
192 BrowserThread::UI, FROM_HERE, 219 // advertise a larger Content-Length than the amount of bytes in the message
193 NewRunnableMethod( 220 // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1,
194 download_manager, &DownloadManager::OnResponseCompleted, 221 // and Safari 5.0.4 - treat the download as complete in this case, so we
195 id, download_file->bytes_so_far(), os_error, hash)); 222 // follow their lead.
223 if (net_error == net::OK || net_error == net::ERR_CONNECTION_CLOSED) {
224 BrowserThread::PostTask(
225 BrowserThread::UI,
226 FROM_HERE,
227 NewRunnableMethod(
228 download_manager,
229 &DownloadManager::OnResponseCompleted,
230 id,
231 download_file->bytes_so_far(),
232 hash));
233 } else {
234 BrowserThread::PostTask(
235 BrowserThread::UI,
236 FROM_HERE,
237 NewRunnableMethod(
238 download_manager,
239 &DownloadManager::OnDownloadError,
240 id,
241 download_file->bytes_so_far(),
242 net_error));
243 }
196 // We need to keep the download around until the UI thread has finalized 244 // We need to keep the download around until the UI thread has finalized
197 // the name. 245 // the name.
198 } 246 }
199 247
200 // This method will be sent via a user action, or shutdown on the UI thread, and 248 // This method will be sent via a user action, or shutdown on the UI thread, and
201 // run on the download thread. Since this message has been sent from the UI 249 // run on the download thread. Since this message has been sent from the UI
202 // thread, the download may have already completed and won't exist in our map. 250 // thread, the download may have already completed and won't exist in our map.
203 void DownloadFileManager::CancelDownload(int id) { 251 void DownloadFileManager::CancelDownload(int id) {
204 VLOG(20) << __FUNCTION__ << "()" << " id = " << id; 252 VLOG(20) << __FUNCTION__ << "()" << " id = " << id;
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 DownloadFile* download_file = GetDownloadFile(id); 319 DownloadFile* download_file = GetDownloadFile(id);
272 if (!download_file) 320 if (!download_file)
273 return; 321 return;
274 322
275 VLOG(20) << __FUNCTION__ << "()" 323 VLOG(20) << __FUNCTION__ << "()"
276 << " download_file = " << download_file->DebugString(); 324 << " download_file = " << download_file->DebugString();
277 325
278 if (!download_file->Rename(full_path)) { 326 if (!download_file->Rename(full_path)) {
279 // Error. Between the time the UI thread generated 'full_path' to the time 327 // Error. Between the time the UI thread generated 'full_path' to the time
280 // this code runs, something happened that prevents us from renaming. 328 // this code runs, something happened that prevents us from renaming.
281 CancelDownloadOnRename(id); 329 CancelDownloadOnRename(id, net::ERR_FAILED);
282 } 330 }
283 } 331 }
284 332
285 // The DownloadManager in the UI thread has provided a final name for the 333 // The DownloadManager in the UI thread has provided a final name for the
286 // download specified by 'id'. Rename the download that's in the process 334 // download specified by 'id'. Rename the download that's in the process
287 // of completing. 335 // of completing.
288 // 336 //
289 // There are 2 possible rename cases where this method can be called: 337 // There are 2 possible rename cases where this method can be called:
290 // 1. foo.crdownload -> foo (final, safe) 338 // 1. foo.crdownload -> foo (final, safe)
291 // 2. Unconfirmed.xxx.crdownload -> xxx (final, validated) 339 // 2. Unconfirmed.xxx.crdownload -> xxx (final, validated)
(...skipping 27 matching lines...) Expand all
319 uniquifier = DownloadFile::GetUniquePathNumber(new_path); 367 uniquifier = DownloadFile::GetUniquePathNumber(new_path);
320 if (uniquifier > 0) { 368 if (uniquifier > 0) {
321 DownloadFile::AppendNumberToPath(&new_path, uniquifier); 369 DownloadFile::AppendNumberToPath(&new_path, uniquifier);
322 } 370 }
323 } 371 }
324 372
325 // Rename the file, overwriting if necessary. 373 // Rename the file, overwriting if necessary.
326 if (!download_file->Rename(new_path)) { 374 if (!download_file->Rename(new_path)) {
327 // Error. Between the time the UI thread generated 'full_path' to the time 375 // Error. Between the time the UI thread generated 'full_path' to the time
328 // this code runs, something happened that prevents us from renaming. 376 // this code runs, something happened that prevents us from renaming.
329 CancelDownloadOnRename(id); 377 CancelDownloadOnRename(id, net::ERR_FAILED);
330 return; 378 return;
331 } 379 }
332 380
333 #if defined(OS_MACOSX) 381 #if defined(OS_MACOSX)
334 // Done here because we only want to do this once; see 382 // Done here because we only want to do this once; see
335 // http://crbug.com/13120 for details. 383 // http://crbug.com/13120 for details.
336 download_file->AnnotateWithSourceInformation(); 384 download_file->AnnotateWithSourceInformation();
337 #endif 385 #endif
338 386
339 BrowserThread::PostTask( 387 BrowserThread::PostTask(
340 BrowserThread::UI, FROM_HERE, 388 BrowserThread::UI, FROM_HERE,
341 NewRunnableMethod( 389 NewRunnableMethod(
342 download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id, 390 download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id,
343 new_path, uniquifier)); 391 new_path, uniquifier));
344 } 392 }
345 393
346 // Called only from RenameInProgressDownloadFile and 394 // Called only from RenameInProgressDownloadFile and
347 // RenameCompletingDownloadFile on the FILE thread. 395 // RenameCompletingDownloadFile on the FILE thread.
348 void DownloadFileManager::CancelDownloadOnRename(int id) { 396 void DownloadFileManager::CancelDownloadOnRename(int id, int rename_error) {
349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
350 398
351 DownloadFile* download_file = GetDownloadFile(id); 399 DownloadFile* download_file = GetDownloadFile(id);
352 if (!download_file) 400 if (!download_file)
353 return; 401 return;
354 402
355 DownloadManager* download_manager = download_file->GetDownloadManager(); 403 DownloadManager* download_manager = download_file->GetDownloadManager();
356 if (!download_manager) { 404 if (!download_manager) {
357 // Without a download manager, we can't cancel the request normally, so we 405 // Without a download manager, we can't cancel the request normally, so we
358 // need to do it here. The normal path will also update the download 406 // need to do it here. The normal path will also update the download
359 // history before cancelling the request. 407 // history before canceling the request.
360 download_file->CancelDownloadRequest(); 408 download_file->CancelDownloadRequest();
361 return; 409 return;
362 } 410 }
363 411
364 BrowserThread::PostTask( 412 BrowserThread::PostTask(
365 BrowserThread::UI, FROM_HERE, 413 BrowserThread::UI, FROM_HERE,
366 NewRunnableMethod(download_manager, 414 NewRunnableMethod(download_manager,
367 &DownloadManager::CancelDownload, id)); 415 &DownloadManager::OnDownloadError,
416 id,
417 download_file->bytes_so_far(),
418 rename_error));
368 } 419 }
369 420
370 void DownloadFileManager::EraseDownload(int id) { 421 void DownloadFileManager::EraseDownload(int id) {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
372 423
373 if (!ContainsKey(downloads_, id)) 424 if (!ContainsKey(downloads_, id))
374 return; 425 return;
375 426
376 DownloadFile* download_file = downloads_[id]; 427 DownloadFile* download_file = downloads_[id];
377 428
378 VLOG(20) << " " << __FUNCTION__ << "()" 429 VLOG(20) << " " << __FUNCTION__ << "()"
379 << " id = " << id 430 << " id = " << id
380 << " download_file = " << download_file->DebugString(); 431 << " download_file = " << download_file->DebugString();
381 432
382 downloads_.erase(id); 433 downloads_.erase(id);
383 434
384 delete download_file; 435 delete download_file;
385 436
386 if (downloads_.empty()) 437 if (downloads_.empty())
387 StopUpdateTimer(); 438 StopUpdateTimer();
388 } 439 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698