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/storage_partition_impl_map.h" | 5 #include "content/browser/storage_partition_impl_map.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
12 #include "base/string_number_conversions.h" | |
12 #include "base/string_util.h" | 13 #include "base/string_util.h" |
13 #include "base/string_number_conversions.h" | 14 #include "base/stringprintf.h" |
14 #include "base/threading/worker_pool.h" | 15 #include "base/threading/worker_pool.h" |
15 #include "content/browser/appcache/chrome_appcache_service.h" | 16 #include "content/browser/appcache/chrome_appcache_service.h" |
16 #include "content/browser/fileapi/browser_file_system_helper.h" | 17 #include "content/browser/fileapi/browser_file_system_helper.h" |
17 #include "content/browser/fileapi/chrome_blob_storage_context.h" | 18 #include "content/browser/fileapi/chrome_blob_storage_context.h" |
18 #include "content/browser/histogram_internals_request_job.h" | 19 #include "content/browser/histogram_internals_request_job.h" |
19 #include "content/browser/net/view_blob_internals_job_factory.h" | 20 #include "content/browser/net/view_blob_internals_job_factory.h" |
20 #include "content/browser/net/view_http_cache_job_factory.h" | 21 #include "content/browser/net/view_http_cache_job_factory.h" |
21 #include "content/browser/renderer_host/resource_request_info_impl.h" | 22 #include "content/browser/renderer_host/resource_request_info_impl.h" |
22 #include "content/browser/resource_context_impl.h" | 23 #include "content/browser/resource_context_impl.h" |
23 #include "content/browser/storage_partition_impl.h" | 24 #include "content/browser/storage_partition_impl.h" |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
203 // | 204 // |
204 // The code in GetStoragePartitionPath() constructs these path names. | 205 // The code in GetStoragePartitionPath() constructs these path names. |
205 // | 206 // |
206 // TODO(nasko): Move extension related path code out of content. | 207 // TODO(nasko): Move extension related path code out of content. |
207 const FilePath::CharType kStoragePartitionDirname[] = | 208 const FilePath::CharType kStoragePartitionDirname[] = |
208 FILE_PATH_LITERAL("Storage"); | 209 FILE_PATH_LITERAL("Storage"); |
209 const FilePath::CharType kExtensionsDirname[] = | 210 const FilePath::CharType kExtensionsDirname[] = |
210 FILE_PATH_LITERAL("ext"); | 211 FILE_PATH_LITERAL("ext"); |
211 const FilePath::CharType kDefaultPartitionDirname[] = | 212 const FilePath::CharType kDefaultPartitionDirname[] = |
212 FILE_PATH_LITERAL("def"); | 213 FILE_PATH_LITERAL("def"); |
214 const FilePath::CharType kTrashDirname[] = | |
215 FILE_PATH_LITERAL("trash"); | |
213 | 216 |
214 // Because partition names are user specified, they can be arbitrarily long | 217 // Because partition names are user specified, they can be arbitrarily long |
215 // which makes them unsuitable for paths names. We use a truncation of a | 218 // which makes them unsuitable for paths names. We use a truncation of a |
216 // SHA256 hash to perform a deterministic shortening of the string. The | 219 // SHA256 hash to perform a deterministic shortening of the string. The |
217 // kPartitionNameHashBytes constant controls the length of the truncation. | 220 // kPartitionNameHashBytes constant controls the length of the truncation. |
218 // We use 6 bytes, which gives us 99.999% reliability against collisions over | 221 // We use 6 bytes, which gives us 99.999% reliability against collisions over |
219 // 1 million partition domains. | 222 // 1 million partition domains. |
220 // | 223 // |
221 // Analysis: | 224 // Analysis: |
222 // We assume that all partition names within one partition domain are | 225 // We assume that all partition names within one partition domain are |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
260 } | 263 } |
261 | 264 |
262 // Helper function for doing a depth-first deletion of the data on disk. | 265 // Helper function for doing a depth-first deletion of the data on disk. |
263 // Examines paths directly in |current_dir| (no recursion) and tries to | 266 // Examines paths directly in |current_dir| (no recursion) and tries to |
264 // delete from disk anything that is in, or isn't a parent of something in | 267 // delete from disk anything that is in, or isn't a parent of something in |
265 // |paths_to_keep|. Paths that need further expansion are added to | 268 // |paths_to_keep|. Paths that need further expansion are added to |
266 // |paths_to_consider|. | 269 // |paths_to_consider|. |
267 void ObliterateOneDirectory(const FilePath& current_dir, | 270 void ObliterateOneDirectory(const FilePath& current_dir, |
268 const std::vector<FilePath>& paths_to_keep, | 271 const std::vector<FilePath>& paths_to_keep, |
269 std::vector<FilePath>* paths_to_consider) { | 272 std::vector<FilePath>* paths_to_consider) { |
273 CHECK(current_dir.IsAbsolute()); | |
274 | |
270 file_util::FileEnumerator enumerator(current_dir, false, kAllFileTypes); | 275 file_util::FileEnumerator enumerator(current_dir, false, kAllFileTypes); |
271 for (FilePath to_delete = enumerator.Next(); !to_delete.empty(); | 276 for (FilePath to_delete = enumerator.Next(); !to_delete.empty(); |
272 to_delete = enumerator.Next()) { | 277 to_delete = enumerator.Next()) { |
273 // Enum tracking which of the 3 possible actions to take for |to_delete|. | 278 // Enum tracking which of the 3 possible actions to take for |to_delete|. |
274 enum { kSkip, kEnqueue, kDelete } action = kDelete; | 279 enum { kSkip, kEnqueue, kDelete } action = kDelete; |
275 | 280 |
276 for (std::vector<FilePath>::const_iterator to_keep = paths_to_keep.begin(); | 281 for (std::vector<FilePath>::const_iterator to_keep = paths_to_keep.begin(); |
277 to_keep != paths_to_keep.end(); | 282 to_keep != paths_to_keep.end(); |
278 ++to_keep) { | 283 ++to_keep) { |
279 if (to_delete == *to_keep) { | 284 if (to_delete == *to_keep) { |
(...skipping 19 matching lines...) Expand all Loading... | |
299 case kSkip: | 304 case kSkip: |
300 break; | 305 break; |
301 } | 306 } |
302 } | 307 } |
303 } | 308 } |
304 | 309 |
305 // Synchronously attempts to delete |root|, preserving only entries in | 310 // Synchronously attempts to delete |root|, preserving only entries in |
306 // |paths_to_keep|. If there are no entries in |paths_to_keep| on disk, then it | 311 // |paths_to_keep|. If there are no entries in |paths_to_keep| on disk, then it |
307 // completely removes |root|. All paths must be absolute paths. | 312 // completely removes |root|. All paths must be absolute paths. |
308 void BlockingObliteratePath(const FilePath& root, | 313 void BlockingObliteratePath(const FilePath& root, |
309 const std::vector<FilePath>& paths_to_keep) { | 314 const std::vector<FilePath>& paths_to_keep, |
315 const base::Closure& on_gc_required) { | |
316 CHECK(root.IsAbsolute()); | |
317 | |
310 // Reduce |paths_to_keep| set to those under the root and actually on disk. | 318 // Reduce |paths_to_keep| set to those under the root and actually on disk. |
311 std::vector<FilePath> valid_paths_to_keep; | 319 std::vector<FilePath> valid_paths_to_keep; |
312 for (std::vector<FilePath>::const_iterator it = paths_to_keep.begin(); | 320 for (std::vector<FilePath>::const_iterator it = paths_to_keep.begin(); |
313 it != paths_to_keep.end(); | 321 it != paths_to_keep.end(); |
314 ++it) { | 322 ++it) { |
315 if (root.IsParent(*it) && file_util::PathExists(*it)) | 323 if (root.IsParent(*it) && file_util::PathExists(*it)) |
316 valid_paths_to_keep.push_back(*it); | 324 valid_paths_to_keep.push_back(*it); |
317 } | 325 } |
318 | 326 |
319 // If none of the |paths_to_keep| are valid anymore then we just whack the | 327 // If none of the |paths_to_keep| are valid anymore then we just whack the |
320 // root and be done with it. | 328 // root and be done with it. |
321 if (valid_paths_to_keep.empty()) { | 329 if (valid_paths_to_keep.empty()) { |
322 file_util::Delete(root, true); | 330 file_util::Delete(root, true); |
323 return; | 331 return; |
332 } else { | |
333 on_gc_required.Run(); | |
324 } | 334 } |
325 | 335 |
326 // Otherwise, start at the root and delete everything that is not in | 336 // Otherwise, start at the root and delete everything that is not in |
327 // |valid_paths_to_keep|. | 337 // |valid_paths_to_keep|. |
328 std::vector<FilePath> paths_to_consider; | 338 std::vector<FilePath> paths_to_consider; |
329 paths_to_consider.push_back(root); | 339 paths_to_consider.push_back(root); |
330 while(!paths_to_consider.empty()) { | 340 while(!paths_to_consider.empty()) { |
331 FilePath path = paths_to_consider.back(); | 341 FilePath path = paths_to_consider.back(); |
332 paths_to_consider.pop_back(); | 342 paths_to_consider.pop_back(); |
333 ObliterateOneDirectory(path, valid_paths_to_keep, &paths_to_consider); | 343 ObliterateOneDirectory(path, valid_paths_to_keep, &paths_to_consider); |
334 } | 344 } |
335 } | 345 } |
336 | 346 |
347 // Deletes all entries inside the |storage_root| that are not in the | |
348 // |active_paths|. Deletion is done in 2 steps: | |
349 // | |
350 // (1) Moving all garbage collected paths into a trash directory. | |
351 // (2) Asynchronously deleting the trash directory. | |
352 // | |
353 // The deletion is asynchronously because after (1) completes, calling code can | |
Charlie Reis
2012/12/05 02:25:40
nit: asynchronous?
| |
354 // continue to use the paths that had just been garbage collected without | |
355 // fear of race conditions. | |
356 // | |
357 // This code also ignores failed moves rather than attempting a smarter retry. | |
358 // Moves shouldn't fail here unless there is some out-of-band error (eg., | |
359 // FS corruption). Retry logic is dangerous in the general case because | |
360 // there is not necessarily a guaranteed case where the logic may succeed. | |
361 void BlockingGarbageCollect( | |
362 const FilePath& storage_root, | |
363 scoped_ptr<base::hash_set<FilePath> > active_paths) { | |
364 CHECK(storage_root.IsAbsolute()); | |
365 | |
366 file_util::FileEnumerator enumerator(storage_root, false, kAllFileTypes); | |
367 FilePath trash_directory; | |
368 if (!file_util::CreateTemporaryDirInDir(storage_root, kTrashDirname, | |
369 &trash_directory)) { | |
370 // Unable to continue without creating the trash directory so give up. | |
371 return; | |
372 } | |
373 for (FilePath path = enumerator.Next(); !path.empty(); | |
374 path = enumerator.Next()) { | |
375 if (active_paths->find(path) == active_paths->end() && | |
376 path != trash_directory) { | |
377 // Since |trash_directory| is unique for each run of this function there | |
378 // can be no colllisions on the move. | |
379 file_util::Move(path, trash_directory.Append(path.BaseName())); | |
380 } | |
381 } | |
382 | |
383 BrowserThread::PostBlockingPoolTask( | |
384 FROM_HERE, | |
385 base::Bind(base::IgnoreResult(&file_util::Delete), trash_directory, | |
386 true)); | |
387 } | |
388 | |
337 } // namespace | 389 } // namespace |
338 | 390 |
339 // static | 391 // static |
340 FilePath StoragePartitionImplMap::GetStoragePartitionPath( | 392 FilePath StoragePartitionImplMap::GetStoragePartitionPath( |
341 const std::string& partition_domain, | 393 const std::string& partition_domain, |
342 const std::string& partition_name) { | 394 const std::string& partition_name) { |
343 if (partition_domain.empty()) | 395 if (partition_domain.empty()) |
344 return FilePath(); | 396 return FilePath(); |
345 | 397 |
346 FilePath path = GetStoragePartitionDomainPath(partition_domain); | 398 FilePath path = GetStoragePartitionDomainPath(partition_domain); |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
411 partition_domain.empty() ? | 463 partition_domain.empty() ? |
412 browser_context_->GetMediaRequestContext() : | 464 browser_context_->GetMediaRequestContext() : |
413 browser_context_->GetMediaRequestContextForStoragePartition( | 465 browser_context_->GetMediaRequestContextForStoragePartition( |
414 partition->GetPath(), in_memory)); | 466 partition->GetPath(), in_memory)); |
415 | 467 |
416 PostCreateInitialization(partition, in_memory); | 468 PostCreateInitialization(partition, in_memory); |
417 | 469 |
418 return partition; | 470 return partition; |
419 } | 471 } |
420 | 472 |
421 void StoragePartitionImplMap::AsyncObliterate(const GURL& site) { | 473 void StoragePartitionImplMap::AsyncObliterate( |
474 const GURL& site, | |
475 const base::Closure& on_gc_required) { | |
422 // This method should avoid creating any StoragePartition (which would | 476 // This method should avoid creating any StoragePartition (which would |
423 // create more open file handles) so that it can delete as much of the | 477 // create more open file handles) so that it can delete as much of the |
424 // data off disk as possible. | 478 // data off disk as possible. |
425 std::string partition_domain; | 479 std::string partition_domain; |
426 std::string partition_name; | 480 std::string partition_name; |
427 bool in_memory = false; | 481 bool in_memory = false; |
428 GetContentClient()->browser()->GetStoragePartitionConfigForSite( | 482 GetContentClient()->browser()->GetStoragePartitionConfigForSite( |
429 browser_context_, site, false, &partition_domain, | 483 browser_context_, site, false, &partition_domain, |
430 &partition_name, &in_memory); | 484 &partition_name, &in_memory); |
431 | 485 |
432 // The default partition is the whole profile. We can't obliterate that and | |
433 // should never even try. | |
434 CHECK(!partition_domain.empty()) << site; | |
435 | |
436 // Find the active partitions for the domain. Because these partitions are | 486 // Find the active partitions for the domain. Because these partitions are |
437 // active, it is not possible to just delete the directories that contain | 487 // active, it is not possible to just delete the directories that contain |
438 // the backing data structures without causing the browser to crash. Instead, | 488 // the backing data structures without causing the browser to crash. Instead, |
439 // of deleteing the directory, we tell each storage context later to | 489 // of deleteing the directory, we tell each storage context later to |
440 // remove any data they have saved. This will leave the directory structure | 490 // remove any data they have saved. This will leave the directory structure |
441 // intact but it will only contain empty databases. | 491 // intact but it will only contain empty databases. |
442 std::vector<StoragePartitionImpl*> active_partitions; | 492 std::vector<StoragePartitionImpl*> active_partitions; |
443 std::vector<FilePath> paths_to_keep; | 493 std::vector<FilePath> paths_to_keep; |
444 for (PartitionMap::const_iterator it = partitions_.begin(); | 494 for (PartitionMap::const_iterator it = partitions_.begin(); |
445 it != partitions_.end(); | 495 it != partitions_.end(); |
446 ++it) { | 496 ++it) { |
447 const StoragePartitionConfig& config = it->first; | 497 const StoragePartitionConfig& config = it->first; |
448 if (config.partition_domain == partition_domain) { | 498 if (config.partition_domain == partition_domain) { |
449 it->second->AsyncClearAllData(); | 499 it->second->AsyncClearAllData(); |
450 if (!config.in_memory) { | 500 if (!config.in_memory) { |
451 paths_to_keep.push_back(it->second->GetPath()); | 501 paths_to_keep.push_back(it->second->GetPath()); |
452 } | 502 } |
453 } | 503 } |
454 } | 504 } |
455 | 505 |
456 // Start a best-effort delete of the on-disk storage excluding paths that are | 506 // Start a best-effort delete of the on-disk storage excluding paths that are |
457 // known to still be in use. This is to delete any previously created | 507 // known to still be in use. This is to delete any previously created |
458 // StoragePartition state that just happens to not have been used during this | 508 // StoragePartition state that just happens to not have been used during this |
459 // run of the browser. | 509 // run of the browser. |
460 FilePath domain_root = browser_context_->GetPath().Append( | 510 FilePath domain_root = browser_context_->GetPath().Append( |
461 GetStoragePartitionDomainPath(partition_domain)); | 511 GetStoragePartitionDomainPath(partition_domain)); |
462 base::WorkerPool::PostTask( | 512 |
513 // Never try to obliterate the browser context root. Die hard. | |
Charlie Reis
2012/12/05 02:25:40
Yippie ki-yay. :)
| |
514 CHECK(domain_root != browser_context_->GetPath()) << site; | |
515 | |
516 BrowserThread::PostBlockingPoolTask( | |
463 FROM_HERE, | 517 FROM_HERE, |
464 base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep), | 518 base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep, |
465 true); | 519 on_gc_required)); |
520 } | |
466 | 521 |
467 // TODO(ajwong): Schedule a final AsyncObliterate of the whole directory on | 522 void StoragePartitionImplMap::GarbageCollect( |
468 // the next browser start. http://crbug.com/85127. | 523 scoped_ptr<base::hash_set<FilePath> > active_paths, |
524 const base::Closure& done) { | |
525 // Include all paths for current StoragePartitions in the active_paths since | |
526 // they cannot be deleted safely. | |
527 for (PartitionMap::const_iterator it = partitions_.begin(); | |
528 it != partitions_.end(); | |
529 ++it) { | |
530 const StoragePartitionConfig& config = it->first; | |
531 if (!config.in_memory) | |
532 active_paths->insert(it->second->GetPath()); | |
533 } | |
534 | |
535 // Find the directory holding the StoragePartitions and delete everything in | |
536 // there that isn't considered active. | |
537 // | |
538 // TODO(ajwong): This should sequence on itself so we create the trash | |
539 // directory safely. | |
540 FilePath storage_root = browser_context_->GetPath().Append( | |
541 GetStoragePartitionDomainPath(std::string())); | |
542 BrowserThread::PostBlockingPoolTaskAndReply( | |
543 FROM_HERE, | |
544 base::Bind(&BlockingGarbageCollect, storage_root, | |
545 base::Passed(&active_paths)), | |
546 done); | |
469 } | 547 } |
470 | 548 |
471 void StoragePartitionImplMap::ForEach( | 549 void StoragePartitionImplMap::ForEach( |
472 const BrowserContext::StoragePartitionCallback& callback) { | 550 const BrowserContext::StoragePartitionCallback& callback) { |
473 for (PartitionMap::const_iterator it = partitions_.begin(); | 551 for (PartitionMap::const_iterator it = partitions_.begin(); |
474 it != partitions_.end(); | 552 it != partitions_.end(); |
475 ++it) { | 553 ++it) { |
476 callback.Run(it->second); | 554 callback.Run(it->second); |
477 } | 555 } |
478 } | 556 } |
(...skipping 27 matching lines...) Expand all Loading... | |
506 | 584 |
507 // We do not call InitializeURLRequestContext() for media contexts because, | 585 // We do not call InitializeURLRequestContext() for media contexts because, |
508 // other than the HTTP cache, the media contexts share the same backing | 586 // other than the HTTP cache, the media contexts share the same backing |
509 // objects as their associated "normal" request context. Thus, the previous | 587 // objects as their associated "normal" request context. Thus, the previous |
510 // call serves to initialize the media request context for this storage | 588 // call serves to initialize the media request context for this storage |
511 // partition as well. | 589 // partition as well. |
512 } | 590 } |
513 } | 591 } |
514 | 592 |
515 } // namespace content | 593 } // namespace content |
OLD | NEW |