Chromium Code Reviews| 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 |